Coverage Report

Created: 2023-06-07 06:04

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