Coverage Report

Created: 2024-07-27 06:19

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