Bug Summary

File:src/usr.bin/tmux/format-draw.c
Warning:line 1161, column 17
The left expression of the compound assignment is an uninitialized value. The computed value will also be garbage

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name format-draw.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/tmux/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.bin/tmux -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/tmux/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/tmux/format-draw.c
1/* $OpenBSD: format-draw.c,v 1.25 2021/10/26 12:22:23 nicm Exp $ */
2
3/*
4 * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20
21#include <stdlib.h>
22#include <string.h>
23
24#include "tmux.h"
25
26/* Format range. */
27struct 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)struct { struct format_range *tqe_next; struct format_range *
*tqe_prev; }
entry;
38};
39TAILQ_HEAD(format_ranges, format_range)struct format_ranges { struct format_range *tqh_first; struct
format_range **tqh_last; }
;
40
41/* Does this range match this style? */
42static int
43format_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. */
54static void
55format_free_range(struct format_ranges *frs, struct format_range *fr)
56{
57 TAILQ_REMOVE(frs, fr, entry)do { if (((fr)->entry.tqe_next) != ((void *)0)) (fr)->entry
.tqe_next->entry.tqe_prev = (fr)->entry.tqe_prev; else (
frs)->tqh_last = (fr)->entry.tqe_prev; *(fr)->entry.
tqe_prev = (fr)->entry.tqe_next; ; ; } while (0)
;
58 free(fr);
59}
60
61/* Fix range positions. */
62static void
63format_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((void *)0))
69 return;
70
71 TAILQ_FOREACH_SAFE(fr, frs, entry, fr1)for ((fr) = ((frs)->tqh_first); (fr) != ((void *)0) &&
((fr1) = ((fr)->entry.tqe_next), 1); (fr) = (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. */
98static void
99format_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. */
113static void
114format_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. */
157static void
158format_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{
162 u_int width_left, width_centre, width_right, width_abs_centre;
163
164 width_left = left->cx;
165 width_centre = centre->cx;
166 width_right = right->cx;
167 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 while (width_left + width_centre + width_right > available) {
174 if (width_centre > 0)
175 width_centre--;
176 else if (width_right > 0)
177 width_right--;
178 else
179 width_left--;
180 }
181
182 /* Write left. */
183 format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
184
185 /* Write right at available - width_right. */
186 format_draw_put(octx, ocx, ocy, right, frs,
187 available - width_right,
188 right->cx - width_right,
189 width_right);
190
191 /*
192 * Write centre halfway between
193 * width_left
194 * and
195 * available - width_right.
196 */
197 format_draw_put(octx, ocx, ocy, centre, frs,
198 width_left
199 + ((available - width_right) - width_left) / 2
200 - width_centre / 2,
201 centre->cx / 2 - width_centre / 2,
202 width_centre);
203
204 /*
205 * Write abs_centre in the perfect centre of all horizontal space.
206 */
207 if (width_abs_centre > available)
208 width_abs_centre = available;
209 format_draw_put(octx, ocx, ocy, abs_centre, frs,
210 (available - width_abs_centre) / 2,
211 0,
212 width_abs_centre);
213}
214
215/* Draw format with list on the left. */
216static void
217format_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{
223 u_int width_left, width_centre, width_right;
224 u_int width_list, width_after, width_abs_centre;
225 struct screen_write_ctx ctx;
226
227 width_left = left->cx;
228 width_centre = centre->cx;
229 width_right = right->cx;
230 width_abs_centre = abs_centre->cx;
231 width_list = list->cx;
232 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 while (width_left +
239 width_centre +
240 width_right +
241 width_list +
242 width_after > available) {
243 if (width_centre > 0)
244 width_centre--;
245 else if (width_list > 0)
246 width_list--;
247 else if (width_right > 0)
248 width_right--;
249 else if (width_after > 0)
250 width_after--;
251 else
252 width_left--;
253 }
254
255 /* If there is no list left, pass off to the no list function. */
256 if (width_list == 0) {
257 screen_write_start(&ctx, left);
258 screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
259 screen_write_stop(&ctx);
260
261 format_draw_none(octx, available, ocx, ocy, left, centre,
262 right, abs_centre, frs);
263 return;
264 }
265
266 /* Write left at 0. */
267 format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
268
269 /* Write right at available - width_right. */
270 format_draw_put(octx, ocx, ocy, right, frs,
271 available - width_right,
272 right->cx - width_right,
273 width_right);
274
275 /* Write after at width_left + width_list. */
276 format_draw_put(octx, ocx, ocy, after, frs,
277 width_left + width_list,
278 0,
279 width_after);
280
281 /*
282 * Write centre halfway between
283 * width_left + width_list + width_after
284 * and
285 * available - width_right.
286 */
287 format_draw_put(octx, ocx, ocy, centre, frs,
288 (width_left + width_list + width_after)
289 + ((available - width_right)
290 - (width_left + width_list + width_after)) / 2
291 - width_centre / 2,
292 centre->cx / 2 - width_centre / 2,
293 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 if (focus_start == -1 || focus_end == -1)
303 focus_start = focus_end = 0;
304 format_draw_put_list(octx, ocx, ocy, width_left, width_list, list,
305 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 if (width_abs_centre > available)
311 width_abs_centre = available;
312 format_draw_put(octx, ocx, ocy, abs_centre, frs,
313 (available - width_abs_centre) / 2,
314 0,
315 width_abs_centre);
316}
317
318/* Draw format with list in the centre. */
319static void
320format_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{
326 u_int width_left, width_centre, width_right, middle;
327 u_int width_list, width_after, width_abs_centre;
328 struct screen_write_ctx ctx;
329
330 width_left = left->cx;
331 width_centre = centre->cx;
332 width_right = right->cx;
333 width_abs_centre = abs_centre->cx;
334 width_list = list->cx;
335 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 while (width_left +
342 width_centre +
343 width_right +
344 width_list +
345 width_after > available) {
346 if (width_list > 0)
347 width_list--;
348 else if (width_after > 0)
349 width_after--;
350 else if (width_centre > 0)
351 width_centre--;
352 else if (width_right > 0)
353 width_right--;
354 else
355 width_left--;
356 }
357
358 /* If there is no list left, pass off to the no list function. */
359 if (width_list == 0) {
360 screen_write_start(&ctx, centre);
361 screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
362 screen_write_stop(&ctx);
363
364 format_draw_none(octx, available, ocx, ocy, left, centre,
365 right, abs_centre, frs);
366 return;
367 }
368
369 /* Write left at 0. */
370 format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
371
372 /* Write right at available - width_right. */
373 format_draw_put(octx, ocx, ocy, right, frs,
374 available - width_right,
375 right->cx - width_right,
376 width_right);
377
378 /*
379 * All three centre sections are offset from the middle of the
380 * available space.
381 */
382 middle = (width_left + ((available - width_right) - width_left) / 2);
383
384 /*
385 * Write centre at
386 * middle - width_list / 2 - width_centre.
387 */
388 format_draw_put(octx, ocx, ocy, centre, frs,
389 middle - width_list / 2 - width_centre,
390 0,
391 width_centre);
392
393 /*
394 * Write after at
395 * middle - width_list / 2 + width_list
396 */
397 format_draw_put(octx, ocx, ocy, after, frs,
398 middle - width_list / 2 + width_list,
399 0,
400 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 if (focus_start == -1 || focus_end == -1)
410 focus_start = focus_end = list->cx / 2;
411 format_draw_put_list(octx, ocx, ocy, middle - width_list / 2,
412 width_list, list, list_left, list_right, focus_start, focus_end,
413 frs);
414
415 /*
416 * Write abs_centre in the perfect centre of all horizontal space.
417 */
418 if (width_abs_centre > available)
419 width_abs_centre = available;
420 format_draw_put(octx, ocx, ocy, abs_centre, frs,
421 (available - width_abs_centre) / 2,
422 0,
423 width_abs_centre);
424}
425
426/* Draw format with list on the right. */
427static void
428format_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{
434 u_int width_left, width_centre, width_right;
435 u_int width_list, width_after, width_abs_centre;
436 struct screen_write_ctx ctx;
437
438 width_left = left->cx;
439 width_centre = centre->cx;
440 width_right = right->cx;
441 width_abs_centre = abs_centre->cx;
442 width_list = list->cx;
443 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 while (width_left +
450 width_centre +
451 width_right +
452 width_list +
453 width_after > available) {
454 if (width_centre > 0)
455 width_centre--;
456 else if (width_list > 0)
457 width_list--;
458 else if (width_right > 0)
459 width_right--;
460 else if (width_after > 0)
461 width_after--;
462 else
463 width_left--;
464 }
465
466 /* If there is no list left, pass off to the no list function. */
467 if (width_list == 0) {
468 screen_write_start(&ctx, right);
469 screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
470 screen_write_stop(&ctx);
471
472 format_draw_none(octx, available, ocx, ocy, left, centre,
473 right, abs_centre, frs);
474 return;
475 }
476
477 /* Write left at 0. */
478 format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
479
480 /* Write after at available - width_after. */
481 format_draw_put(octx, ocx, ocy, after, frs,
482 available - width_after,
483 after->cx - width_after,
484 width_after);
485
486 /*
487 * Write right at
488 * available - width_right - width_list - width_after.
489 */
490 format_draw_put(octx, ocx, ocy, right, frs,
491 available - width_right - width_list - width_after,
492 0,
493 width_right);
494
495 /*
496 * Write centre halfway between
497 * width_left
498 * and
499 * available - width_right - width_list - width_after.
500 */
501 format_draw_put(octx, ocx, ocy, centre, frs,
502 width_left
503 + ((available - width_right - width_list - width_after)
504 - width_left) / 2
505 - width_centre / 2,
506 centre->cx / 2 - width_centre / 2,
507 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 if (focus_start == -1 || focus_end == -1)
517 focus_start = focus_end = 0;
518 format_draw_put_list(octx, ocx, ocy, available - width_list -
519 width_after, width_list, list, list_left, list_right, focus_start,
520 focus_end, frs);
521
522 /*
523 * Write abs_centre in the perfect centre of all horizontal space.
524 */
525 if (width_abs_centre > available)
526 width_abs_centre = available;
527 format_draw_put(octx, ocx, ocy, abs_centre, frs,
528 (available - width_abs_centre) / 2,
529 0,
530 width_abs_centre);
531}
532
533static void
534format_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{
540 u_int width_left, width_centre, width_right, width_abs_centre;
541 u_int width_list, width_after, middle, abs_centre_offset;
542
543 width_left = left->cx;
544 width_centre = centre->cx;
545 width_right = right->cx;
546 width_abs_centre = abs_centre->cx;
547 width_list = list->cx;
548 width_after = after->cx;
549
550 /*
551 * Trim first centre, then the right, then the left.
552 */
553 while (width_left +
554 width_centre +
555 width_right > available) {
556 if (width_centre > 0)
557 width_centre--;
558 else if (width_right > 0)
559 width_right--;
560 else
561 width_left--;
562 }
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 while (width_list + width_after + width_abs_centre > available) {
570 if (width_list > 0)
571 width_list--;
572 else if (width_after > 0)
573 width_after--;
574 else
575 width_abs_centre--;
576 }
577
578 /* Write left at 0. */
579 format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
580
581 /* Write right at available - width_right. */
582 format_draw_put(octx, ocx, ocy, right, frs,
583 available - width_right,
584 right->cx - width_right,
585 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 middle = (width_left + ((available - width_right) - width_left) / 2);
592
593 /*
594 * Write centre at
595 * middle - width_centre.
596 */
597 format_draw_put(octx, ocx, ocy, centre, frs,
598 middle - width_centre,
599 0,
600 width_centre);
601
602 /*
603 * If there is no focus given, keep the centre in focus.
604 */
605 if (focus_start == -1 || focus_end == -1)
606 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 abs_centre_offset = (available - width_list - width_abs_centre) / 2;
613
614 /*
615 * Write abs_centre before the list.
616 */
617 format_draw_put(octx, ocx, ocy, abs_centre, frs, abs_centre_offset,
618 0, width_abs_centre);
619 abs_centre_offset += width_abs_centre;
620
621 /*
622 * Draw the list in the absolute centre
623 */
624 format_draw_put_list(octx, ocx, ocy, abs_centre_offset, width_list,
625 list, list_left, list_right, focus_start, focus_end, frs);
626 abs_centre_offset += width_list;
627
628 /*
629 * Write after at the end of the centre
630 */
631 format_draw_put(octx, ocx, ocy, after, frs, abs_centre_offset, 0,
632 width_after);
633}
634
635/* Get width and count of any leading #s. */
636static const char *
637format_leading_hashes(const char *cp, u_int *n, u_int *width)
638{
639 for (*n = 0; cp[*n] == '#'; (*n)++)
640 /* nothing */;
641 if (*n == 0) {
642 *width = 0;
643 return (cp);
644 }
645 if (cp[*n] != '[') {
646 if ((*n % 2) == 0)
647 *width = (*n / 2);
648 else
649 *width = (*n / 2) + 1;
650 return (cp + *n);
651 }
652 *width = (*n / 2);
653 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 return (cp + *n);
660 }
661 /* This is a style, so return pointing to the #. */
662 return (cp + *n - 1);
663}
664
665/* Draw multiple characters. */
666static void
667format_draw_many(struct screen_write_ctx *ctx, struct style *sy, char ch,
668 u_int n)
669{
670 u_int i;
671
672 utf8_set(&sy->gc.data, ch);
673 for (i = 0; i < n; i++)
674 screen_write_cell(ctx, &sy->gc);
675}
676
677/* Draw a format to a screen. */
678void
679format_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{
683 enum { LEFT,
684 CENTRE,
685 RIGHT,
686 ABSOLUTE_CENTRE,
687 LIST,
688 LIST_LEFT,
689 LIST_RIGHT,
690 AFTER,
691 TOTAL } current = LEFT, last = LEFT;
692 const char *names[] = { "LEFT",
693 "CENTRE",
694 "RIGHT",
695 "ABSOLUTE_CENTRE",
696 "LIST",
697 "LIST_LEFT",
698 "LIST_RIGHT",
699 "AFTER" };
700 size_t size = strlen(expanded);
701 struct screen *os = octx->s, s[TOTAL];
702 struct screen_write_ctx ctx[TOTAL];
703 u_int ocx = os->cx, ocy = os->cy, n, i, width[TOTAL];
704 u_int map[] = { LEFT,
705 LEFT,
706 CENTRE,
707 RIGHT,
708 ABSOLUTE_CENTRE };
709 int focus_start = -1, focus_end = -1;
710 int list_state = -1, fill = -1, even;
711 enum style_align list_align = STYLE_ALIGN_DEFAULT;
712 struct grid_cell gc, current_default;
713 struct style sy, saved_sy;
714 struct utf8_data *ud = &sy.gc.data;
715 const char *cp, *end;
716 enum utf8_state more;
717 char *tmp;
718 struct format_range *fr = NULL((void *)0), *fr1;
719 struct format_ranges frs;
720 struct style_range *sr;
721
722 memcpy(&current_default, base, sizeof current_default);
723 style_set(&sy, &current_default);
724 TAILQ_INIT(&frs)do { (&frs)->tqh_first = ((void *)0); (&frs)->tqh_last
= &(&frs)->tqh_first; } while (0)
;
725 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 for (i = 0; i < TOTAL; i++) {
733 screen_init(&s[i], size, 1, 0);
734 screen_write_start(&ctx[i], &s[i]);
735 screen_write_clearendofline(&ctx[i], current_default.bg);
736 width[i] = 0;
737 }
738
739 /*
740 * Walk the string and add to the corresponding screens,
741 * parsing styles as we go.
742 */
743 cp = expanded;
744 while (*cp != '\0') {
745 /* Handle sequences of #. */
746 if (cp[0] == '#' && cp[1] != '[' && cp[1] != '\0') {
747 for (n = 1; cp[n] == '#'; n++)
748 /* nothing */;
749 even = ((n % 2) == 0);
750 if (cp[n] != '[') {
751 cp += n;
752 if (even)
753 n = (n / 2);
754 else
755 n = (n / 2) + 1;
756 width[current] += n;
757 format_draw_many(&ctx[current], &sy, '#', n);
758 continue;
759 }
760 if (even)
761 cp += (n + 1);
762 else
763 cp += (n - 1);
764 if (sy.ignore)
765 continue;
766 format_draw_many(&ctx[current], &sy, '#', n / 2);
767 width[current] += (n / 2);
768 if (even) {
769 utf8_set(ud, '[');
770 screen_write_cell(&ctx[current], &sy.gc);
771 width[current]++;
772 }
773 continue;
774 }
775
776 /* Is this not a style? */
777 if (cp[0] != '#' || cp[1] != '[' || sy.ignore) {
778 /* See if this is a UTF-8 character. */
779 if ((more = utf8_open(ud, *cp)) == UTF8_MORE) {
780 while (*++cp != '\0' && more == UTF8_MORE)
781 more = utf8_append(ud, *cp);
782 if (more != UTF8_DONE)
783 cp -= ud->have;
784 }
785
786 /* Not a UTF-8 character - ASCII or not valid. */
787 if (more != UTF8_DONE) {
788 if (*cp < 0x20 || *cp > 0x7e) {
789 /* Ignore nonprintable characters. */
790 cp++;
791 continue;
792 }
793 utf8_set(ud, *cp);
794 cp++;
795 }
796
797 /* Draw the cell to the current screen. */
798 screen_write_cell(&ctx[current], &sy.gc);
799 width[current] += ud->width;
800 continue;
801 }
802
803 /* This is a style. Work out where the end is and parse it. */
804 end = format_skip(cp + 2, "]");
805 if (end == NULL((void *)0)) {
806 log_debug("%s: no terminating ] at '%s'", __func__,
807 cp + 2);
808 TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1)for ((fr) = ((&frs)->tqh_first); (fr) != ((void *)0) &&
((fr1) = ((fr)->entry.tqe_next), 1); (fr) = (fr1))
809 format_free_range(&frs, fr);
810 goto out;
811 }
812 tmp = xstrndup(cp + 2, end - (cp + 2));
813 style_copy(&saved_sy, &sy);
814 if (style_parse(&sy, &current_default, tmp) != 0) {
815 log_debug("%s: invalid style '%s'", __func__, tmp);
816 free(tmp);
817 cp = end + 1;
818 continue;
819 }
820 log_debug("%s: style '%s' -> '%s'", __func__, tmp,
821 style_tostring(&sy));
822 free(tmp);
823 if (default_colours) {
824 sy.gc.bg = base->bg;
825 sy.gc.fg = base->fg;
826 }
827
828 /* If this style has a fill colour, store it for later. */
829 if (sy.fill != 8)
830 fill = sy.fill;
831
832 /* If this style pushed or popped the default, update it. */
833 if (sy.default_type == STYLE_DEFAULT_PUSH) {
834 memcpy(&current_default, &saved_sy.gc,
835 sizeof current_default);
836 sy.default_type = STYLE_DEFAULT_BASE;
837 } else if (sy.default_type == STYLE_DEFAULT_POP) {
838 memcpy(&current_default, base, sizeof current_default);
839 sy.default_type = STYLE_DEFAULT_BASE;
840 }
841
842 /* Check the list state. */
843 switch (sy.list) {
844 case STYLE_LIST_ON:
845 /*
846 * Entering the list, exiting a marker, or exiting the
847 * focus.
848 */
849 if (list_state != 0) {
850 if (fr != NULL((void *)0)) { /* abort any region */
851 free(fr);
852 fr = NULL((void *)0);
853 }
854 list_state = 0;
855 list_align = sy.align;
856 }
857
858 /* End the focus if started. */
859 if (focus_start != -1 && focus_end == -1)
860 focus_end = s[LIST].cx;
861
862 current = LIST;
863 break;
864 case STYLE_LIST_FOCUS:
865 /* Entering the focus. */
866 if (list_state != 0) /* not inside the list */
867 break;
868 if (focus_start == -1) /* focus already started */
869 focus_start = s[LIST].cx;
870 break;
871 case STYLE_LIST_OFF:
872 /* Exiting or outside the list. */
873 if (list_state == 0) {
874 if (fr != NULL((void *)0)) { /* abort any region */
875 free(fr);
876 fr = NULL((void *)0);
877 }
878 if (focus_start != -1 && focus_end == -1)
879 focus_end = s[LIST].cx;
880
881 map[list_align] = AFTER;
882 if (list_align == STYLE_ALIGN_LEFT)
883 map[STYLE_ALIGN_DEFAULT] = AFTER;
884 list_state = 1;
885 }
886 current = map[sy.align];
887 break;
888 case STYLE_LIST_LEFT_MARKER:
889 /* Entering left marker. */
890 if (list_state != 0) /* not inside the list */
891 break;
892 if (s[LIST_LEFT].cx != 0) /* already have marker */
893 break;
894 if (fr != NULL((void *)0)) { /* abort any region */
895 free(fr);
896 fr = NULL((void *)0);
897 }
898 if (focus_start != -1 && focus_end == -1)
899 focus_start = focus_end = -1;
900 current = LIST_LEFT;
901 break;
902 case STYLE_LIST_RIGHT_MARKER:
903 /* Entering right marker. */
904 if (list_state != 0) /* not inside the list */
905 break;
906 if (s[LIST_RIGHT].cx != 0) /* already have marker */
907 break;
908 if (fr != NULL((void *)0)) { /* abort any region */
909 free(fr);
910 fr = NULL((void *)0);
911 }
912 if (focus_start != -1 && focus_end == -1)
913 focus_start = focus_end = -1;
914 current = LIST_RIGHT;
915 break;
916 }
917 if (current != last) {
918 log_debug("%s: change %s -> %s", __func__,
919 names[last], names[current]);
920 last = current;
921 }
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 if (srs != NULL((void *)0)) {
928 if (fr != NULL((void *)0) && !format_is_type(fr, &sy)) {
929 if (s[current].cx != fr->start) {
930 fr->end = s[current].cx + 1;
931 TAILQ_INSERT_TAIL(&frs, fr, entry)do { (fr)->entry.tqe_next = ((void *)0); (fr)->entry.tqe_prev
= (&frs)->tqh_last; *(&frs)->tqh_last = (fr); (
&frs)->tqh_last = &(fr)->entry.tqe_next; } while
(0)
;
932 } else
933 free(fr);
934 fr = NULL((void *)0);
935 }
936 if (fr == NULL((void *)0) && sy.range_type != STYLE_RANGE_NONE) {
937 fr = xcalloc(1, sizeof *fr);
938 fr->index = current;
939
940 fr->s = &s[current];
941 fr->start = s[current].cx;
942
943 fr->type = sy.range_type;
944 fr->argument = sy.range_argument;
945 }
946 }
947
948 cp = end + 1;
949 }
950 free(fr);
951
952 for (i = 0; i < TOTAL; i++) {
953 screen_write_stop(&ctx[i]);
954 log_debug("%s: width %s is %u", __func__, names[i], width[i]);
955 }
956 if (focus_start != -1 && focus_end != -1)
957 log_debug("%s: focus %d-%d", __func__, focus_start, focus_end);
958 TAILQ_FOREACH(fr, &frs, entry)for((fr) = ((&frs)->tqh_first); (fr) != ((void *)0); (
fr) = ((fr)->entry.tqe_next))
{
959 log_debug("%s: range %d|%u is %s %u-%u", __func__, fr->type,
960 fr->argument, names[fr->index], fr->start, fr->end);
961 }
962
963 /* Clear the available area. */
964 if (fill != -1) {
965 memcpy(&gc, &grid_default_cell, sizeof gc);
966 gc.bg = fill;
967 for (i = 0; i < available; i++)
968 screen_write_putc(octx, &gc, ' ');
969 }
970
971 /*
972 * Draw the screens. How they are arranged depends on where the list
973 * appears.
974 */
975 switch (list_align) {
976 case STYLE_ALIGN_DEFAULT:
977 /* No list. */
978 format_draw_none(octx, available, ocx, ocy, &s[LEFT],
979 &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &frs);
980 break;
981 case STYLE_ALIGN_LEFT:
982 /* List is part of the left. */
983 format_draw_left(octx, available, ocx, ocy, &s[LEFT],
984 &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
985 &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
986 focus_start, focus_end, &frs);
987 break;
988 case STYLE_ALIGN_CENTRE:
989 /* List is part of the centre. */
990 format_draw_centre(octx, available, ocx, ocy, &s[LEFT],
991 &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
992 &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
993 focus_start, focus_end, &frs);
994 break;
995 case STYLE_ALIGN_RIGHT:
996 /* List is part of the right. */
997 format_draw_right(octx, available, ocx, ocy, &s[LEFT],
998 &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
999 &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
1000 focus_start, focus_end, &frs);
1001 break;
1002 case STYLE_ALIGN_ABSOLUTE_CENTRE:
1003 /* List is in the centre of the entire horizontal space. */
1004 format_draw_absolute_centre(octx, available, ocx, ocy, &s[LEFT],
1005 &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
1006 &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
1007 focus_start, focus_end, &frs);
1008 break;
1009 }
1010
1011 /* Create ranges to return. */
1012 TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1)for ((fr) = ((&frs)->tqh_first); (fr) != ((void *)0) &&
((fr1) = ((fr)->entry.tqe_next), 1); (fr) = (fr1))
{
1013 sr = xcalloc(1, sizeof *sr);
1014 sr->type = fr->type;
1015 sr->argument = fr->argument;
1016 sr->start = fr->start;
1017 sr->end = fr->end;
1018 TAILQ_INSERT_TAIL(srs, sr, entry)do { (sr)->entry.tqe_next = ((void *)0); (sr)->entry.tqe_prev
= (srs)->tqh_last; *(srs)->tqh_last = (sr); (srs)->
tqh_last = &(sr)->entry.tqe_next; } while (0)
;
1019
1020 log_debug("%s: range %d|%u at %u-%u", __func__, sr->type,
1021 sr->argument, sr->start, sr->end);
1022
1023 format_free_range(&frs, fr);
1024 }
1025
1026out:
1027 /* Free the screens. */
1028 for (i = 0; i < TOTAL; i++)
1029 screen_free(&s[i]);
1030
1031 /* Restore the original cursor position. */
1032 screen_write_cursormove(octx, ocx, ocy, 0);
1033}
1034
1035/* Get width, taking #[] into account. */
1036u_int
1037format_width(const char *expanded)
1038{
1039 const char *cp, *end;
1040 u_int n, leading_width, width = 0;
1041 struct utf8_data ud;
1042 enum utf8_state more;
1043
1044 cp = expanded;
1045 while (*cp != '\0') {
1046 if (*cp == '#') {
1047 end = format_leading_hashes(cp, &n, &leading_width);
1048 width += leading_width;
1049 cp = end;
1050 if (*cp == '#') {
1051 end = format_skip(cp + 2, "]");
1052 if (end == NULL((void *)0))
1053 return (0);
1054 cp = end + 1;
1055 }
1056 } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
1057 while (*++cp != '\0' && more == UTF8_MORE)
1058 more = utf8_append(&ud, *cp);
1059 if (more == UTF8_DONE)
1060 width += ud.width;
1061 else
1062 cp -= ud.have;
1063 } else if (*cp > 0x1f && *cp < 0x7f) {
1064 width++;
1065 cp++;
1066 } else
1067 cp++;
1068 }
1069 return (width);
1070}
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 */
1077char *
1078format_trim_left(const char *expanded, u_int limit)
1079{
1080 char *copy, *out;
1081 const char *cp = expanded, *end;
1082 u_int n, width = 0, leading_width;
1083 struct utf8_data ud;
1084 enum utf8_state more;
1085
1086 out = copy = xcalloc(1, strlen(expanded) + 1);
1087 while (*cp != '\0') {
1088 if (width >= limit)
1089 break;
1090 if (*cp == '#') {
1091 end = format_leading_hashes(cp, &n, &leading_width);
1092 if (leading_width > limit - width)
1093 leading_width = limit - width;
1094 if (leading_width != 0) {
1095 if (n == 1)
1096 *out++ = '#';
1097 else {
1098 memset(out, '#', 2 * leading_width);
1099 out += 2 * leading_width;
1100 }
1101 width += leading_width;
1102 }
1103 cp = end;
1104 if (*cp == '#') {
1105 end = format_skip(cp + 2, "]");
1106 if (end == NULL((void *)0))
1107 break;
1108 memcpy(out, cp, end + 1 - cp);
1109 out += (end + 1 - cp);
1110 cp = end + 1;
1111 }
1112 } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
1113 while (*++cp != '\0' && more == UTF8_MORE)
1114 more = utf8_append(&ud, *cp);
1115 if (more == UTF8_DONE) {
1116 if (width + ud.width <= limit) {
1117 memcpy(out, ud.data, ud.size);
1118 out += ud.size;
1119 }
1120 width += ud.width;
1121 } else {
1122 cp -= ud.have;
1123 cp++;
1124 }
1125 } else if (*cp > 0x1f && *cp < 0x7f) {
1126 if (width + 1 <= limit)
1127 *out++ = *cp;
1128 width++;
1129 cp++;
1130 } else
1131 cp++;
1132 }
1133 *out = '\0';
1134 return (copy);
1135}
1136
1137/* Trim on the right, taking #[] into account. */
1138char *
1139format_trim_right(const char *expanded, u_int limit)
1140{
1141 char *copy, *out;
1142 const char *cp = expanded, *end;
1143 u_int width = 0, total_width, skip, n;
1144 u_int leading_width, copy_width;
1
'copy_width' declared without an initial value
1145 struct utf8_data ud;
1146 enum utf8_state more;
1147
1148 total_width = format_width(expanded);
1149 if (total_width <= limit)
2
Assuming 'total_width' is > 'limit'
3
Taking false branch
1150 return (xstrdup(expanded));
1151 skip = total_width - limit;
1152
1153 out = copy = xcalloc(1, strlen(expanded) + 1);
1154 while (*cp != '\0') {
4
Assuming the condition is true
5
Loop condition is true. Entering loop body
1155 if (*cp == '#') {
6
Assuming the condition is true
7
Taking true branch
1156 end = format_leading_hashes(cp, &n, &leading_width);
1157 if (width
7.1
'width' is <= 'skip'
<= skip) {
8
Taking true branch
1158 if (skip - width >= leading_width)
9
Assuming the condition is false
10
Taking false branch
1159 copy_width = 0;
1160 else
1161 copy_width -= (skip - width);
11
The left expression of the compound assignment is an uninitialized value. The computed value will also be garbage
1162 } else
1163 copy_width = leading_width;
1164 if (copy_width != 0) {
1165 if (n == 1)
1166 *out++ = '#';
1167 else {
1168 memset(out, '#', 2 * copy_width);
1169 out += 2 * copy_width;
1170 }
1171 }
1172 width += leading_width;
1173 cp = end;
1174 if (*cp == '#') {
1175 end = format_skip(cp + 2, "]");
1176 if (end == NULL((void *)0))
1177 break;
1178 memcpy(out, cp, end + 1 - cp);
1179 out += (end + 1 - cp);
1180 cp = end + 1;
1181 }
1182 } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
1183 while (*++cp != '\0' && more == UTF8_MORE)
1184 more = utf8_append(&ud, *cp);
1185 if (more == UTF8_DONE) {
1186 if (width >= skip) {
1187 memcpy(out, ud.data, ud.size);
1188 out += ud.size;
1189 }
1190 width += ud.width;
1191 } else {
1192 cp -= ud.have;
1193 cp++;
1194 }
1195 } else if (*cp > 0x1f && *cp < 0x7f) {
1196 if (width >= skip)
1197 *out++ = *cp;
1198 width++;
1199 cp++;
1200 } else
1201 cp++;
1202 }
1203 *out = '\0';
1204 return (copy);
1205}