1 /* $OpenBSD$ */ 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, NULL, 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, NULL, 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, NULL, 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], NULL, &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] != '[') { 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, sizeof current_default); 604 sy.default_type = STYLE_DEFAULT_BASE; 605 } else if (sy.default_type == STYLE_DEFAULT_POP) { 606 memcpy(¤t_default, base, sizeof current_default); 607 sy.default_type = STYLE_DEFAULT_BASE; 608 } 609 610 /* Check the list state. */ 611 switch (sy.list) { 612 case STYLE_LIST_ON: 613 /* 614 * Entering the list, exiting a marker, or exiting the 615 * focus. 616 */ 617 if (list_state != 0) { 618 if (fr != NULL) { /* abort any region */ 619 free(fr); 620 fr = NULL; 621 } 622 list_state = 0; 623 list_align = sy.align; 624 } 625 626 /* End the focus if started. */ 627 if (focus_start != -1 && focus_end == -1) 628 focus_end = s[LIST].cx; 629 630 current = LIST; 631 break; 632 case STYLE_LIST_FOCUS: 633 /* Entering the focus. */ 634 if (list_state != 0) /* not inside the list */ 635 break; 636 if (focus_start == -1) /* focus already started */ 637 focus_start = s[LIST].cx; 638 break; 639 case STYLE_LIST_OFF: 640 /* Exiting or outside the list. */ 641 if (list_state == 0) { 642 if (fr != NULL) { /* abort any region */ 643 free(fr); 644 fr = NULL; 645 } 646 if (focus_start != -1 && focus_end == -1) 647 focus_end = s[LIST].cx; 648 649 map[list_align] = AFTER; 650 if (list_align == STYLE_ALIGN_LEFT) 651 map[STYLE_ALIGN_DEFAULT] = AFTER; 652 list_state = 1; 653 } 654 current = map[sy.align]; 655 break; 656 case STYLE_LIST_LEFT_MARKER: 657 /* Entering left marker. */ 658 if (list_state != 0) /* not inside the list */ 659 break; 660 if (s[LIST_LEFT].cx != 0) /* already have marker */ 661 break; 662 if (fr != NULL) { /* abort any region */ 663 free(fr); 664 fr = NULL; 665 } 666 if (focus_start != -1 && focus_end == -1) 667 focus_start = focus_end = -1; 668 current = LIST_LEFT; 669 break; 670 case STYLE_LIST_RIGHT_MARKER: 671 /* Entering right marker. */ 672 if (list_state != 0) /* not inside the list */ 673 break; 674 if (s[LIST_RIGHT].cx != 0) /* already have marker */ 675 break; 676 if (fr != NULL) { /* abort any region */ 677 free(fr); 678 fr = NULL; 679 } 680 if (focus_start != -1 && focus_end == -1) 681 focus_start = focus_end = -1; 682 current = LIST_RIGHT; 683 break; 684 } 685 if (current != last) { 686 log_debug("%s: change %s -> %s", __func__, 687 names[last], names[current]); 688 last = current; 689 } 690 691 /* 692 * Check if the range style has changed and if so end the 693 * current range and start a new one if needed. 694 */ 695 if (srs != NULL) { 696 if (fr != NULL && !format_is_type(fr, &sy)) { 697 if (s[current].cx != fr->start) { 698 fr->end = s[current].cx + 1; 699 TAILQ_INSERT_TAIL(&frs, fr, entry); 700 } else 701 free(fr); 702 fr = NULL; 703 } 704 if (fr == NULL && sy.range_type != STYLE_RANGE_NONE) { 705 fr = xcalloc(1, sizeof *fr); 706 fr->index = current; 707 708 fr->s = &s[current]; 709 fr->start = s[current].cx; 710 711 fr->type = sy.range_type; 712 fr->argument = sy.range_argument; 713 } 714 } 715 716 cp = end + 1; 717 } 718 free(fr); 719 720 for (i = 0; i < TOTAL; i++) { 721 screen_write_stop(&ctx[i]); 722 log_debug("%s: width %s is %u", __func__, names[i], width[i]); 723 } 724 if (focus_start != -1 && focus_end != -1) 725 log_debug("%s: focus %d-%d", __func__, focus_start, focus_end); 726 TAILQ_FOREACH(fr, &frs, entry) { 727 log_debug("%s: range %d|%u is %s %u-%u", __func__, fr->type, 728 fr->argument, names[fr->index], fr->start, fr->end); 729 } 730 731 /* Clear the available area. */ 732 if (fill != -1) { 733 memcpy(&gc, &grid_default_cell, sizeof gc); 734 gc.bg = fill; 735 for (i = 0; i < available; i++) 736 screen_write_putc(octx, &gc, ' '); 737 } 738 739 /* 740 * Draw the screens. How they are arranged depends on where the list 741 * appearsq. 742 */ 743 switch (list_align) { 744 case STYLE_ALIGN_DEFAULT: 745 /* No list. */ 746 format_draw_none(octx, available, ocx, ocy, &s[LEFT], 747 &s[CENTRE], &s[RIGHT], &frs); 748 break; 749 case STYLE_ALIGN_LEFT: 750 /* List is part of the left. */ 751 format_draw_left(octx, available, ocx, ocy, &s[LEFT], 752 &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT], 753 &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs); 754 break; 755 case STYLE_ALIGN_CENTRE: 756 /* List is part of the centre. */ 757 format_draw_centre(octx, available, ocx, ocy, &s[LEFT], 758 &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT], 759 &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs); 760 break; 761 case STYLE_ALIGN_RIGHT: 762 /* List is part of the right. */ 763 format_draw_right(octx, available, ocx, ocy, &s[LEFT], 764 &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT], 765 &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs); 766 break; 767 } 768 769 /* Create ranges to return. */ 770 TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) { 771 sr = xcalloc(1, sizeof *sr); 772 sr->type = fr->type; 773 sr->argument = fr->argument; 774 sr->start = fr->start; 775 sr->end = fr->end; 776 TAILQ_INSERT_TAIL(srs, sr, entry); 777 778 log_debug("%s: range %d|%u at %u-%u", __func__, sr->type, 779 sr->argument, sr->start, sr->end); 780 781 format_free_range(&frs, fr); 782 } 783 784 out: 785 /* Free the screens. */ 786 for (i = 0; i < TOTAL; i++) 787 screen_free(&s[i]); 788 789 /* Restore the original cursor position. */ 790 screen_write_cursormove(octx, ocx, ocy, 0); 791 } 792 793 /* Get width, taking #[] into account. */ 794 u_int 795 format_width(const char *expanded) 796 { 797 const char *cp, *end; 798 u_int width = 0; 799 struct utf8_data ud; 800 enum utf8_state more; 801 802 cp = expanded; 803 while (*cp != '\0') { 804 if (cp[0] == '#' && cp[1] == '[') { 805 end = format_skip(cp + 2, "]"); 806 if (end == NULL) 807 return 0; 808 cp = end + 1; 809 } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { 810 while (*++cp != '\0' && more == UTF8_MORE) 811 more = utf8_append(&ud, *cp); 812 if (more == UTF8_DONE) 813 width += ud.width; 814 else 815 cp -= ud.have; 816 } else if (*cp > 0x1f && *cp < 0x7f) { 817 width++; 818 cp++; 819 } else 820 cp++; 821 } 822 return (width); 823 } 824 825 /* Trim on the left, taking #[] into account. */ 826 char * 827 format_trim_left(const char *expanded, u_int limit) 828 { 829 char *copy, *out; 830 const char *cp = expanded, *end; 831 u_int width = 0; 832 struct utf8_data ud; 833 enum utf8_state more; 834 835 out = copy = xmalloc(strlen(expanded) + 1); 836 while (*cp != '\0') { 837 if (cp[0] == '#' && cp[1] == '[') { 838 end = format_skip(cp + 2, "]"); 839 if (end == NULL) 840 break; 841 memcpy(out, cp, end + 1 - cp); 842 out += (end + 1 - cp); 843 cp = end + 1; 844 } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { 845 while (*++cp != '\0' && more == UTF8_MORE) 846 more = utf8_append(&ud, *cp); 847 if (more == UTF8_DONE) { 848 if (width + ud.width <= limit) { 849 memcpy(out, ud.data, ud.size); 850 out += ud.size; 851 } 852 width += ud.width; 853 } else { 854 cp -= ud.have; 855 cp++; 856 } 857 } else if (*cp > 0x1f && *cp < 0x7f) { 858 if (width + 1 <= limit) 859 *out++ = *cp; 860 width++; 861 cp++; 862 } else 863 cp++; 864 } 865 *out = '\0'; 866 return (copy); 867 } 868 869 /* Trim on the right, taking #[] into account. */ 870 char * 871 format_trim_right(const char *expanded, u_int limit) 872 { 873 char *copy, *out; 874 const char *cp = expanded, *end; 875 u_int width = 0, total_width, skip; 876 struct utf8_data ud; 877 enum utf8_state more; 878 879 total_width = format_width(expanded); 880 if (total_width <= limit) 881 return (xstrdup(expanded)); 882 skip = total_width - limit; 883 884 out = copy = xmalloc(strlen(expanded) + 1); 885 while (*cp != '\0') { 886 if (cp[0] == '#' && cp[1] == '[') { 887 end = format_skip(cp + 2, "]"); 888 if (end == NULL) 889 break; 890 memcpy(out, cp, end + 1 - cp); 891 out += (end + 1 - cp); 892 cp = end + 1; 893 } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { 894 while (*++cp != '\0' && more == UTF8_MORE) 895 more = utf8_append(&ud, *cp); 896 if (more == UTF8_DONE) { 897 if (width >= skip) { 898 memcpy(out, ud.data, ud.size); 899 out += ud.size; 900 } 901 width += ud.width; 902 } else { 903 cp -= ud.have; 904 cp++; 905 } 906 } else if (*cp > 0x1f && *cp < 0x7f) { 907 if (width >= skip) 908 *out++ = *cp; 909 width++; 910 cp++; 911 } else 912 cp++; 913 } 914 *out = '\0'; 915 return (copy); 916 } 917