Bug Summary

File:src/usr.bin/tmux/mode-tree.c
Warning:line 754, column 27
Access to field 'name' results in a dereference of a null pointer (loaded from variable 'mti')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name mode-tree.c -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 -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -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/llvm16/lib/clang/16 -I /usr/src/usr.bin/tmux -internal-isystem /usr/local/llvm16/lib/clang/16/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 -fcf-protection=branch -fno-jump-tables -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/scan/2024-01-11-140451-98009-1 -x c /usr/src/usr.bin/tmux/mode-tree.c
1/* $OpenBSD: mode-tree.c,v 1.66 2023/08/15 07:01:47 nicm Exp $ */
2
3/*
4 * Copyright (c) 2017 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 <ctype.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include "tmux.h"
27
28struct mode_tree_item;
29TAILQ_HEAD(mode_tree_list, mode_tree_item)struct mode_tree_list { struct mode_tree_item *tqh_first; struct
mode_tree_item **tqh_last; }
;
30
31struct mode_tree_data {
32 int dead;
33 u_int references;
34 int zoomed;
35
36 struct window_pane *wp;
37 void *modedata;
38 const struct menu_item *menu;
39
40 const char **sort_list;
41 u_int sort_size;
42 struct mode_tree_sort_criteria sort_crit;
43
44 mode_tree_build_cb buildcb;
45 mode_tree_draw_cb drawcb;
46 mode_tree_search_cb searchcb;
47 mode_tree_menu_cb menucb;
48 mode_tree_height_cb heightcb;
49 mode_tree_key_cb keycb;
50
51 struct mode_tree_list children;
52 struct mode_tree_list saved;
53
54 struct mode_tree_line *line_list;
55 u_int line_size;
56
57 u_int depth;
58
59 u_int width;
60 u_int height;
61
62 u_int offset;
63 u_int current;
64
65 struct screen screen;
66
67 int preview;
68 char *search;
69 char *filter;
70 int no_matches;
71};
72
73struct mode_tree_item {
74 struct mode_tree_item *parent;
75 void *itemdata;
76 u_int line;
77
78 key_code key;
79 const char *keystr;
80 size_t keylen;
81
82 uint64_t tag;
83 const char *name;
84 const char *text;
85
86 int expanded;
87 int tagged;
88
89 int draw_as_parent;
90 int no_tag;
91
92 struct mode_tree_list children;
93 TAILQ_ENTRY(mode_tree_item)struct { struct mode_tree_item *tqe_next; struct mode_tree_item
**tqe_prev; }
entry;
94};
95
96struct mode_tree_line {
97 struct mode_tree_item *item;
98 u_int depth;
99 int last;
100 int flat;
101};
102
103struct mode_tree_menu {
104 struct mode_tree_data *data;
105 struct client *c;
106 u_int line;
107};
108
109static void mode_tree_free_items(struct mode_tree_list *);
110
111static const struct menu_item mode_tree_menu_items[] = {
112 { "Scroll Left", '<', NULL((void *)0) },
113 { "Scroll Right", '>', NULL((void *)0) },
114 { "", KEYC_NONE0x000ff000000000ULL, NULL((void *)0) },
115 { "Cancel", 'q', NULL((void *)0) },
116
117 { NULL((void *)0), KEYC_NONE0x000ff000000000ULL, NULL((void *)0) }
118};
119
120static struct mode_tree_item *
121mode_tree_find_item(struct mode_tree_list *mtl, uint64_t tag)
122{
123 struct mode_tree_item *mti, *child;
124
125 TAILQ_FOREACH(mti, mtl, entry)for((mti) = ((mtl)->tqh_first); (mti) != ((void *)0); (mti
) = ((mti)->entry.tqe_next))
{
126 if (mti->tag == tag)
127 return (mti);
128 child = mode_tree_find_item(&mti->children, tag);
129 if (child != NULL((void *)0))
130 return (child);
131 }
132 return (NULL((void *)0));
133}
134
135static void
136mode_tree_free_item(struct mode_tree_item *mti)
137{
138 mode_tree_free_items(&mti->children);
139
140 free((void *)mti->name);
141 free((void *)mti->text);
142 free((void *)mti->keystr);
143
144 free(mti);
145}
146
147static void
148mode_tree_free_items(struct mode_tree_list *mtl)
149{
150 struct mode_tree_item *mti, *mti1;
151
152 TAILQ_FOREACH_SAFE(mti, mtl, entry, mti1)for ((mti) = ((mtl)->tqh_first); (mti) != ((void *)0) &&
((mti1) = ((mti)->entry.tqe_next), 1); (mti) = (mti1))
{
153 TAILQ_REMOVE(mtl, mti, entry)do { if (((mti)->entry.tqe_next) != ((void *)0)) (mti)->
entry.tqe_next->entry.tqe_prev = (mti)->entry.tqe_prev;
else (mtl)->tqh_last = (mti)->entry.tqe_prev; *(mti)->
entry.tqe_prev = (mti)->entry.tqe_next; ; ; } while (0)
;
154 mode_tree_free_item(mti);
155 }
156}
157
158static void
159mode_tree_check_selected(struct mode_tree_data *mtd)
160{
161 /*
162 * If the current line would now be off screen reset the offset to the
163 * last visible line.
164 */
165 if (mtd->current > mtd->height - 1)
166 mtd->offset = mtd->current - mtd->height + 1;
167}
168
169static void
170mode_tree_clear_lines(struct mode_tree_data *mtd)
171{
172 free(mtd->line_list);
173 mtd->line_list = NULL((void *)0);
174 mtd->line_size = 0;
175}
176
177static void
178mode_tree_build_lines(struct mode_tree_data *mtd,
179 struct mode_tree_list *mtl, u_int depth)
180{
181 struct mode_tree_item *mti;
182 struct mode_tree_line *line;
183 u_int i;
184 int flat = 1;
185
186 mtd->depth = depth;
187 TAILQ_FOREACH(mti, mtl, entry)for((mti) = ((mtl)->tqh_first); (mti) != ((void *)0); (mti
) = ((mti)->entry.tqe_next))
{
188 mtd->line_list = xreallocarray(mtd->line_list,
189 mtd->line_size + 1, sizeof *mtd->line_list);
190
191 line = &mtd->line_list[mtd->line_size++];
192 line->item = mti;
193 line->depth = depth;
194 line->last = (mti == TAILQ_LAST(mtl, mode_tree_list)(*(((struct mode_tree_list *)((mtl)->tqh_last))->tqh_last
))
);
195
196 mti->line = (mtd->line_size - 1);
197 if (!TAILQ_EMPTY(&mti->children)(((&mti->children)->tqh_first) == ((void *)0)))
198 flat = 0;
199 if (mti->expanded)
200 mode_tree_build_lines(mtd, &mti->children, depth + 1);
201
202 if (mtd->keycb != NULL((void *)0)) {
203 mti->key = mtd->keycb(mtd->modedata, mti->itemdata,
204 mti->line);
205 if (mti->key == KEYC_UNKNOWN0x000fe000000000ULL)
206 mti->key = KEYC_NONE0x000ff000000000ULL;
207 } else if (mti->line < 10)
208 mti->key = '0' + mti->line;
209 else if (mti->line < 36)
210 mti->key = KEYC_META0x00100000000000ULL|('a' + mti->line - 10);
211 else
212 mti->key = KEYC_NONE0x000ff000000000ULL;
213 if (mti->key != KEYC_NONE0x000ff000000000ULL) {
214 mti->keystr = xstrdup(key_string_lookup_key(mti->key,
215 0));
216 mti->keylen = strlen(mti->keystr);
217 } else {
218 mti->keystr = NULL((void *)0);
219 mti->keylen = 0;
220 }
221 }
222 TAILQ_FOREACH(mti, mtl, entry)for((mti) = ((mtl)->tqh_first); (mti) != ((void *)0); (mti
) = ((mti)->entry.tqe_next))
{
223 for (i = 0; i < mtd->line_size; i++) {
224 line = &mtd->line_list[i];
225 if (line->item == mti)
226 line->flat = flat;
227 }
228 }
229}
230
231static void
232mode_tree_clear_tagged(struct mode_tree_list *mtl)
233{
234 struct mode_tree_item *mti;
235
236 TAILQ_FOREACH(mti, mtl, entry)for((mti) = ((mtl)->tqh_first); (mti) != ((void *)0); (mti
) = ((mti)->entry.tqe_next))
{
237 mti->tagged = 0;
238 mode_tree_clear_tagged(&mti->children);
239 }
240}
241
242void
243mode_tree_up(struct mode_tree_data *mtd, int wrap)
244{
245 if (mtd->current == 0) {
246 if (wrap) {
247 mtd->current = mtd->line_size - 1;
248 if (mtd->line_size >= mtd->height)
249 mtd->offset = mtd->line_size - mtd->height;
250 }
251 } else {
252 mtd->current--;
253 if (mtd->current < mtd->offset)
254 mtd->offset--;
255 }
256}
257
258void
259mode_tree_down(struct mode_tree_data *mtd, int wrap)
260{
261 if (mtd->current == mtd->line_size - 1) {
262 if (wrap) {
263 mtd->current = 0;
264 mtd->offset = 0;
265 }
266 } else {
267 mtd->current++;
268 if (mtd->current > mtd->offset + mtd->height - 1)
269 mtd->offset++;
270 }
271}
272
273void *
274mode_tree_get_current(struct mode_tree_data *mtd)
275{
276 return (mtd->line_list[mtd->current].item->itemdata);
277}
278
279const char *
280mode_tree_get_current_name(struct mode_tree_data *mtd)
281{
282 return (mtd->line_list[mtd->current].item->name);
283}
284
285void
286mode_tree_expand_current(struct mode_tree_data *mtd)
287{
288 if (!mtd->line_list[mtd->current].item->expanded) {
289 mtd->line_list[mtd->current].item->expanded = 1;
290 mode_tree_build(mtd);
291 }
292}
293
294void
295mode_tree_collapse_current(struct mode_tree_data *mtd)
296{
297 if (mtd->line_list[mtd->current].item->expanded) {
298 mtd->line_list[mtd->current].item->expanded = 0;
299 mode_tree_build(mtd);
300 }
301}
302
303static int
304mode_tree_get_tag(struct mode_tree_data *mtd, uint64_t tag, u_int *found)
305{
306 u_int i;
307
308 for (i = 0; i < mtd->line_size; i++) {
309 if (mtd->line_list[i].item->tag == tag)
310 break;
311 }
312 if (i != mtd->line_size) {
313 *found = i;
314 return (1);
315 }
316 return (0);
317}
318
319void
320mode_tree_expand(struct mode_tree_data *mtd, uint64_t tag)
321{
322 u_int found;
323
324 if (!mode_tree_get_tag(mtd, tag, &found))
325 return;
326 if (!mtd->line_list[found].item->expanded) {
327 mtd->line_list[found].item->expanded = 1;
328 mode_tree_build(mtd);
329 }
330}
331
332int
333mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag)
334{
335 u_int found;
336
337 if (mode_tree_get_tag(mtd, tag, &found)) {
338 mtd->current = found;
339 if (mtd->current > mtd->height - 1)
340 mtd->offset = mtd->current - mtd->height + 1;
341 else
342 mtd->offset = 0;
343 return (1);
344 }
345 mtd->current = 0;
346 mtd->offset = 0;
347 return (0);
348}
349
350u_int
351mode_tree_count_tagged(struct mode_tree_data *mtd)
352{
353 struct mode_tree_item *mti;
354 u_int i, tagged;
355
356 tagged = 0;
357 for (i = 0; i < mtd->line_size; i++) {
358 mti = mtd->line_list[i].item;
359 if (mti->tagged)
360 tagged++;
361 }
362 return (tagged);
363}
364
365void
366mode_tree_each_tagged(struct mode_tree_data *mtd, mode_tree_each_cb cb,
367 struct client *c, key_code key, int current)
368{
369 struct mode_tree_item *mti;
370 u_int i;
371 int fired;
372
373 fired = 0;
374 for (i = 0; i < mtd->line_size; i++) {
375 mti = mtd->line_list[i].item;
376 if (mti->tagged) {
377 fired = 1;
378 cb(mtd->modedata, mti->itemdata, c, key);
379 }
380 }
381 if (!fired && current) {
382 mti = mtd->line_list[mtd->current].item;
383 cb(mtd->modedata, mti->itemdata, c, key);
384 }
385}
386
387struct mode_tree_data *
388mode_tree_start(struct window_pane *wp, struct args *args,
389 mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb,
390 mode_tree_search_cb searchcb, mode_tree_menu_cb menucb,
391 mode_tree_height_cb heightcb, mode_tree_key_cb keycb, void *modedata,
392 const struct menu_item *menu, const char **sort_list, u_int sort_size,
393 struct screen **s)
394{
395 struct mode_tree_data *mtd;
396 const char *sort;
397 u_int i;
398
399 mtd = xcalloc(1, sizeof *mtd);
400 mtd->references = 1;
401
402 mtd->wp = wp;
403 mtd->modedata = modedata;
404 mtd->menu = menu;
405
406 mtd->sort_list = sort_list;
407 mtd->sort_size = sort_size;
408
409 mtd->preview = !args_has(args, 'N');
410
411 sort = args_get(args, 'O');
412 if (sort != NULL((void *)0)) {
413 for (i = 0; i < sort_size; i++) {
414 if (strcasecmp(sort, sort_list[i]) == 0)
415 mtd->sort_crit.field = i;
416 }
417 }
418 mtd->sort_crit.reversed = args_has(args, 'r');
419
420 if (args_has(args, 'f'))
421 mtd->filter = xstrdup(args_get(args, 'f'));
422 else
423 mtd->filter = NULL((void *)0);
424
425 mtd->buildcb = buildcb;
426 mtd->drawcb = drawcb;
427 mtd->searchcb = searchcb;
428 mtd->menucb = menucb;
429 mtd->heightcb = heightcb;
430 mtd->keycb = keycb;
431
432 TAILQ_INIT(&mtd->children)do { (&mtd->children)->tqh_first = ((void *)0); (&
mtd->children)->tqh_last = &(&mtd->children)
->tqh_first; } while (0)
;
433
434 *s = &mtd->screen;
435 screen_init(*s, screen_size_x(&wp->base)((&wp->base)->grid->sx), screen_size_y(&wp->base)((&wp->base)->grid->sy), 0);
436 (*s)->mode &= ~MODE_CURSOR0x1;
437
438 return (mtd);
439}
440
441void
442mode_tree_zoom(struct mode_tree_data *mtd, struct args *args)
443{
444 struct window_pane *wp = mtd->wp;
445
446 if (args_has(args, 'Z')) {
447 mtd->zoomed = (wp->window->flags & WINDOW_ZOOMED0x8);
448 if (!mtd->zoomed && window_zoom(wp) == 0)
449 server_redraw_window(wp->window);
450 } else
451 mtd->zoomed = -1;
452}
453
454static void
455mode_tree_set_height(struct mode_tree_data *mtd)
456{
457 struct screen *s = &mtd->screen;
458 u_int height;
459
460 if (mtd->heightcb != NULL((void *)0)) {
461 height = mtd->heightcb(mtd, screen_size_y(s)((s)->grid->sy));
462 if (height < screen_size_y(s)((s)->grid->sy))
463 mtd->height = screen_size_y(s)((s)->grid->sy) - height;
464 } else {
465 mtd->height = (screen_size_y(s)((s)->grid->sy) / 3) * 2;
466 if (mtd->height > mtd->line_size)
467 mtd->height = screen_size_y(s)((s)->grid->sy) / 2;
468 }
469 if (mtd->height < 10)
470 mtd->height = screen_size_y(s)((s)->grid->sy);
471 if (screen_size_y(s)((s)->grid->sy) - mtd->height < 2)
472 mtd->height = screen_size_y(s)((s)->grid->sy);
473}
474
475void
476mode_tree_build(struct mode_tree_data *mtd)
477{
478 struct screen *s = &mtd->screen;
479 uint64_t tag;
480
481 if (mtd->line_list != NULL((void *)0))
482 tag = mtd->line_list[mtd->current].item->tag;
483 else
484 tag = UINT64_MAX0xffffffffffffffffULL;
485
486 TAILQ_CONCAT(&mtd->saved, &mtd->children, entry)do { if (!(((&mtd->children)->tqh_first) == ((void *
)0))) { *(&mtd->saved)->tqh_last = (&mtd->children
)->tqh_first; (&mtd->children)->tqh_first->entry
.tqe_prev = (&mtd->saved)->tqh_last; (&mtd->
saved)->tqh_last = (&mtd->children)->tqh_last; do
{ ((&mtd->children))->tqh_first = ((void *)0); ((&
mtd->children))->tqh_last = &((&mtd->children
))->tqh_first; } while (0); } } while (0)
;
487 TAILQ_INIT(&mtd->children)do { (&mtd->children)->tqh_first = ((void *)0); (&
mtd->children)->tqh_last = &(&mtd->children)
->tqh_first; } while (0)
;
488
489 mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, mtd->filter);
490 mtd->no_matches = TAILQ_EMPTY(&mtd->children)(((&mtd->children)->tqh_first) == ((void *)0));
491 if (mtd->no_matches)
492 mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, NULL((void *)0));
493
494 mode_tree_free_items(&mtd->saved);
495 TAILQ_INIT(&mtd->saved)do { (&mtd->saved)->tqh_first = ((void *)0); (&
mtd->saved)->tqh_last = &(&mtd->saved)->tqh_first
; } while (0)
;
496
497 mode_tree_clear_lines(mtd);
498 mode_tree_build_lines(mtd, &mtd->children, 0);
499
500 if (mtd->line_list != NULL((void *)0) && tag == UINT64_MAX0xffffffffffffffffULL)
501 tag = mtd->line_list[mtd->current].item->tag;
502 mode_tree_set_current(mtd, tag);
503
504 mtd->width = screen_size_x(s)((s)->grid->sx);
505 if (mtd->preview)
506 mode_tree_set_height(mtd);
507 else
508 mtd->height = screen_size_y(s)((s)->grid->sy);
509 mode_tree_check_selected(mtd);
510}
511
512static void
513mode_tree_remove_ref(struct mode_tree_data *mtd)
514{
515 if (--mtd->references == 0)
516 free(mtd);
517}
518
519void
520mode_tree_free(struct mode_tree_data *mtd)
521{
522 struct window_pane *wp = mtd->wp;
523
524 if (mtd->zoomed == 0)
525 server_unzoom_window(wp->window);
526
527 mode_tree_free_items(&mtd->children);
528 mode_tree_clear_lines(mtd);
529 screen_free(&mtd->screen);
530
531 free(mtd->search);
532 free(mtd->filter);
533
534 mtd->dead = 1;
535 mode_tree_remove_ref(mtd);
536}
537
538void
539mode_tree_resize(struct mode_tree_data *mtd, u_int sx, u_int sy)
540{
541 struct screen *s = &mtd->screen;
542
543 screen_resize(s, sx, sy, 0);
544
545 mode_tree_build(mtd);
546 mode_tree_draw(mtd);
547
548 mtd->wp->flags |= PANE_REDRAW0x1;
549}
550
551struct mode_tree_item *
552mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
553 void *itemdata, uint64_t tag, const char *name, const char *text,
554 int expanded)
555{
556 struct mode_tree_item *mti, *saved;
557
558 log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag,
559 name, (text == NULL((void *)0) ? "" : text));
560
561 mti = xcalloc(1, sizeof *mti);
562 mti->parent = parent;
563 mti->itemdata = itemdata;
564
565 mti->tag = tag;
566 mti->name = xstrdup(name);
567 if (text != NULL((void *)0))
568 mti->text = xstrdup(text);
569
570 saved = mode_tree_find_item(&mtd->saved, tag);
571 if (saved != NULL((void *)0)) {
572 if (parent == NULL((void *)0) || parent->expanded)
573 mti->tagged = saved->tagged;
574 mti->expanded = saved->expanded;
575 } else if (expanded == -1)
576 mti->expanded = 1;
577 else
578 mti->expanded = expanded;
579
580 TAILQ_INIT(&mti->children)do { (&mti->children)->tqh_first = ((void *)0); (&
mti->children)->tqh_last = &(&mti->children)
->tqh_first; } while (0)
;
581
582 if (parent != NULL((void *)0))
583 TAILQ_INSERT_TAIL(&parent->children, mti, entry)do { (mti)->entry.tqe_next = ((void *)0); (mti)->entry.
tqe_prev = (&parent->children)->tqh_last; *(&parent
->children)->tqh_last = (mti); (&parent->children
)->tqh_last = &(mti)->entry.tqe_next; } while (0)
;
584 else
585 TAILQ_INSERT_TAIL(&mtd->children, mti, entry)do { (mti)->entry.tqe_next = ((void *)0); (mti)->entry.
tqe_prev = (&mtd->children)->tqh_last; *(&mtd->
children)->tqh_last = (mti); (&mtd->children)->tqh_last
= &(mti)->entry.tqe_next; } while (0)
;
586
587 return (mti);
588}
589
590void
591mode_tree_draw_as_parent(struct mode_tree_item *mti)
592{
593 mti->draw_as_parent = 1;
594}
595
596void
597mode_tree_no_tag(struct mode_tree_item *mti)
598{
599 mti->no_tag = 1;
600}
601
602void
603mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti)
604{
605 struct mode_tree_item *parent = mti->parent;
606
607 if (parent != NULL((void *)0))
608 TAILQ_REMOVE(&parent->children, mti, entry)do { if (((mti)->entry.tqe_next) != ((void *)0)) (mti)->
entry.tqe_next->entry.tqe_prev = (mti)->entry.tqe_prev;
else (&parent->children)->tqh_last = (mti)->entry
.tqe_prev; *(mti)->entry.tqe_prev = (mti)->entry.tqe_next
; ; ; } while (0)
;
609 else
610 TAILQ_REMOVE(&mtd->children, mti, entry)do { if (((mti)->entry.tqe_next) != ((void *)0)) (mti)->
entry.tqe_next->entry.tqe_prev = (mti)->entry.tqe_prev;
else (&mtd->children)->tqh_last = (mti)->entry.
tqe_prev; *(mti)->entry.tqe_prev = (mti)->entry.tqe_next
; ; ; } while (0)
;
611 mode_tree_free_item(mti);
612}
613
614void
615mode_tree_draw(struct mode_tree_data *mtd)
616{
617 struct window_pane *wp = mtd->wp;
618 struct screen *s = &mtd->screen;
619 struct mode_tree_line *line;
620 struct mode_tree_item *mti;
621 struct options *oo = wp->window->options;
622 struct screen_write_ctx ctx;
623 struct grid_cell gc0, gc;
624 u_int w, h, i, j, sy, box_x, box_y, width;
625 char *text, *start, *key;
626 const char *tag, *symbol;
627 size_t size, n;
628 int keylen, pad;
629
630 if (mtd->line_size == 0)
6
Assuming field 'line_size' is not equal to 0
7
Taking false branch
631 return;
632
633 memcpy(&gc0, &grid_default_cell, sizeof gc0);
634 memcpy(&gc, &grid_default_cell, sizeof gc);
635 style_apply(&gc, oo, "mode-style", NULL((void *)0));
636
637 w = mtd->width;
638 h = mtd->height;
639
640 screen_write_start(&ctx, s);
641 screen_write_clearscreen(&ctx, 8);
642
643 keylen = 0;
644 for (i = 0; i < mtd->line_size; i++) {
8
Assuming 'i' is < field 'line_size'
9
Loop condition is true. Entering loop body
14
Assuming 'i' is >= field 'line_size'
15
Loop condition is false. Execution continues on line 652
645 mti = mtd->line_list[i].item;
646 if (mti->key == KEYC_NONE0x000ff000000000ULL)
10
Assuming field 'key' is not equal to KEYC_NONE
11
Taking false branch
647 continue;
648 if ((int)mti->keylen + 3 > keylen)
12
Assuming the condition is false
13
Taking false branch
649 keylen = mti->keylen + 3;
650 }
651
652 for (i = 0; i < mtd->line_size; i++) {
16
Loop condition is true. Entering loop body
47
Loop condition is false. Execution continues on line 737
653 if (i < mtd->offset)
17
Assuming 'i' is >= field 'offset'
18
Taking false branch
654 continue;
655 if (i > mtd->offset + h - 1)
19
Assuming the condition is false
20
Taking false branch
656 break;
657 line = &mtd->line_list[i];
658 mti = line->item;
659
660 screen_write_cursormove(&ctx, 0, i - mtd->offset, 0);
661
662 pad = keylen - 2 - mti->keylen;
663 if (mti->key
20.1
Field 'key' is not equal to KEYC_NONE
!= KEYC_NONE0x000ff000000000ULL)
21
Taking true branch
664 xasprintf(&key, "(%s)%*s", mti->keystr, pad, "");
665 else
666 key = xstrdup("");
667
668 if (line->flat)
22
Assuming field 'flat' is 0
23
Taking false branch
669 symbol = "";
670 else if (TAILQ_EMPTY(&mti->children)(((&mti->children)->tqh_first) == ((void *)0)))
24
Assuming field 'tqh_first' is not equal to null
25
Taking false branch
671 symbol = " ";
672 else if (mti->expanded)
26
Assuming field 'expanded' is 0
27
Taking false branch
673 symbol = "- ";
674 else
675 symbol = "+ ";
676
677 if (line->depth == 0)
28
Assuming field 'depth' is not equal to 0
29
Taking false branch
678 start = xstrdup(symbol);
679 else {
680 size = (4 * line->depth) + 32;
681
682 start = xcalloc(1, size);
683 for (j = 1; j < line->depth; j++) {
30
Assuming 'j' is < field 'depth'
32
Assuming 'j' is >= field 'depth'
33
Loop condition is false. Execution continues on line 690
684 if (mti->parent != NULL((void *)0) &&
31
Assuming field 'parent' is equal to NULL
685 mtd->line_list[mti->parent->line].last)
686 strlcat(start, " ", size);
687 else
688 strlcat(start, "\001x\001 ", size);
689 }
690 if (line->last)
34
Assuming field 'last' is 0
35
Taking false branch
691 strlcat(start, "\001mq\001> ", size);
692 else
693 strlcat(start, "\001tq\001> ", size);
694 strlcat(start, symbol, size);
695 }
696
697 if (mti->tagged)
36
Assuming field 'tagged' is 0
37
Taking false branch
698 tag = "*";
699 else
700 tag = "";
701 xasprintf(&text, "%-*s%s%s%s%s", keylen, key, start, mti->name,
702 tag, (mti->text != NULL((void *)0)) ? ": " : "" );
38
Assuming field 'text' is equal to NULL
39
'?' condition is false
703 width = utf8_cstrwidth(text);
704 if (width > w)
40
Assuming 'width' is <= 'w'
41
Taking false branch
705 width = w;
706 free(start);
707
708 if (mti->tagged
41.1
Field 'tagged' is 0
) {
42
Taking false branch
709 gc.attr ^= GRID_ATTR_BRIGHT0x1;
710 gc0.attr ^= GRID_ATTR_BRIGHT0x1;
711 }
712
713 if (i != mtd->current) {
43
Assuming 'i' is equal to field 'current'
44
Taking false branch
714 screen_write_clearendofline(&ctx, 8);
715 screen_write_nputs(&ctx, w, &gc0, "%s", text);
716 if (mti->text != NULL((void *)0)) {
717 format_draw(&ctx, &gc0, w - width, mti->text,
718 NULL((void *)0), 0);
719 }
720 } else {
721 screen_write_clearendofline(&ctx, gc.bg);
722 screen_write_nputs(&ctx, w, &gc, "%s", text);
723 if (mti->text
44.1
Field 'text' is equal to NULL
!= NULL((void *)0)) {
45
Taking false branch
724 format_draw(&ctx, &gc, w - width, mti->text,
725 NULL((void *)0), 0);
726 }
727 }
728 free(text);
729 free(key);
730
731 if (mti->tagged
45.1
Field 'tagged' is 0
) {
46
Taking false branch
732 gc.attr ^= GRID_ATTR_BRIGHT0x1;
733 gc0.attr ^= GRID_ATTR_BRIGHT0x1;
734 }
735 }
736
737 sy = screen_size_y(s)((s)->grid->sy);
738 if (!mtd->preview || sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4)
48
Assuming field 'preview' is not equal to 0
49
Assuming 'sy' is > 4
50
Assuming 'h' is > 4
51
Assuming the condition is false
52
Assuming 'w' is > 4
53
Taking false branch
739 goto done;
740
741 line = &mtd->line_list[mtd->current];
742 mti = line->item;
743 if (mti->draw_as_parent)
54
Assuming field 'draw_as_parent' is not equal to 0
55
Taking true branch
744 mti = mti->parent;
56
Null pointer value stored to 'mti'
745
746 screen_write_cursormove(&ctx, 0, h, 0);
747 screen_write_box(&ctx, w, sy - h, BOX_LINES_DEFAULT, NULL((void *)0), NULL((void *)0));
748
749 if (mtd->sort_list != NULL((void *)0)) {
57
Assuming field 'sort_list' is equal to NULL
58
Taking false branch
750 xasprintf(&text, " %s (sort: %s%s)", mti->name,
751 mtd->sort_list[mtd->sort_crit.field],
752 mtd->sort_crit.reversed ? ", reversed" : "");
753 } else
754 xasprintf(&text, " %s", mti->name);
59
Access to field 'name' results in a dereference of a null pointer (loaded from variable 'mti')
755 if (w - 2 >= strlen(text)) {
756 screen_write_cursormove(&ctx, 1, h, 0);
757 screen_write_puts(&ctx, &gc0, "%s", text);
758
759 if (mtd->no_matches)
760 n = (sizeof "no matches") - 1;
761 else
762 n = (sizeof "active") - 1;
763 if (mtd->filter != NULL((void *)0) && w - 2 >= strlen(text) + 10 + n + 2) {
764 screen_write_puts(&ctx, &gc0, " (filter: ");
765 if (mtd->no_matches)
766 screen_write_puts(&ctx, &gc, "no matches");
767 else
768 screen_write_puts(&ctx, &gc0, "active");
769 screen_write_puts(&ctx, &gc0, ") ");
770 } else
771 screen_write_puts(&ctx, &gc0, " ");
772 }
773 free(text);
774
775 box_x = w - 4;
776 box_y = sy - h - 2;
777
778 if (box_x != 0 && box_y != 0) {
779 screen_write_cursormove(&ctx, 2, h + 1, 0);
780 mtd->drawcb(mtd->modedata, mti->itemdata, &ctx, box_x, box_y);
781 }
782
783done:
784 screen_write_cursormove(&ctx, 0, mtd->current - mtd->offset, 0);
785 screen_write_stop(&ctx);
786}
787
788static struct mode_tree_item *
789mode_tree_search_for(struct mode_tree_data *mtd)
790{
791 struct mode_tree_item *mti, *last, *next;
792
793 if (mtd->search == NULL((void *)0))
794 return (NULL((void *)0));
795
796 mti = last = mtd->line_list[mtd->current].item;
797 for (;;) {
798 if (!TAILQ_EMPTY(&mti->children)(((&mti->children)->tqh_first) == ((void *)0)))
799 mti = TAILQ_FIRST(&mti->children)((&mti->children)->tqh_first);
800 else if ((next = TAILQ_NEXT(mti, entry)((mti)->entry.tqe_next)) != NULL((void *)0))
801 mti = next;
802 else {
803 for (;;) {
804 mti = mti->parent;
805 if (mti == NULL((void *)0))
806 break;
807 if ((next = TAILQ_NEXT(mti, entry)((mti)->entry.tqe_next)) != NULL((void *)0)) {
808 mti = next;
809 break;
810 }
811 }
812 }
813 if (mti == NULL((void *)0))
814 mti = TAILQ_FIRST(&mtd->children)((&mtd->children)->tqh_first);
815 if (mti == last)
816 break;
817
818 if (mtd->searchcb == NULL((void *)0)) {
819 if (strstr(mti->name, mtd->search) != NULL((void *)0))
820 return (mti);
821 continue;
822 }
823 if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search))
824 return (mti);
825 }
826 return (NULL((void *)0));
827}
828
829static void
830mode_tree_search_set(struct mode_tree_data *mtd)
831{
832 struct mode_tree_item *mti, *loop;
833 uint64_t tag;
834
835 mti = mode_tree_search_for(mtd);
836 if (mti == NULL((void *)0))
837 return;
838 tag = mti->tag;
839
840 loop = mti->parent;
841 while (loop != NULL((void *)0)) {
842 loop->expanded = 1;
843 loop = loop->parent;
844 }
845
846 mode_tree_build(mtd);
847 mode_tree_set_current(mtd, tag);
848 mode_tree_draw(mtd);
849 mtd->wp->flags |= PANE_REDRAW0x1;
850}
851
852static int
853mode_tree_search_callback(__unused__attribute__((__unused__)) struct client *c, void *data, const char *s,
854 __unused__attribute__((__unused__)) int done)
855{
856 struct mode_tree_data *mtd = data;
857
858 if (mtd->dead)
859 return (0);
860
861 free(mtd->search);
862 if (s == NULL((void *)0) || *s == '\0') {
863 mtd->search = NULL((void *)0);
864 return (0);
865 }
866 mtd->search = xstrdup(s);
867 mode_tree_search_set(mtd);
868
869 return (0);
870}
871
872static void
873mode_tree_search_free(void *data)
874{
875 mode_tree_remove_ref(data);
876}
877
878static int
879mode_tree_filter_callback(__unused__attribute__((__unused__)) struct client *c, void *data, const char *s,
880 __unused__attribute__((__unused__)) int done)
881{
882 struct mode_tree_data *mtd = data;
883
884 if (mtd->dead)
1
Assuming field 'dead' is 0
2
Taking false branch
885 return (0);
886
887 if (mtd->filter != NULL((void *)0))
3
Assuming field 'filter' is equal to NULL
888 free(mtd->filter);
889 if (s == NULL((void *)0) || *s == '\0')
4
Assuming 's' is equal to NULL
890 mtd->filter = NULL((void *)0);
891 else
892 mtd->filter = xstrdup(s);
893
894 mode_tree_build(mtd);
895 mode_tree_draw(mtd);
5
Calling 'mode_tree_draw'
896 mtd->wp->flags |= PANE_REDRAW0x1;
897
898 return (0);
899}
900
901static void
902mode_tree_filter_free(void *data)
903{
904 mode_tree_remove_ref(data);
905}
906
907static void
908mode_tree_menu_callback(__unused__attribute__((__unused__)) struct menu *menu, __unused__attribute__((__unused__)) u_int idx,
909 key_code key, void *data)
910{
911 struct mode_tree_menu *mtm = data;
912 struct mode_tree_data *mtd = mtm->data;
913
914 if (mtd->dead || key == KEYC_NONE0x000ff000000000ULL)
915 goto out;
916
917 if (mtm->line >= mtd->line_size)
918 goto out;
919 mtd->current = mtm->line;
920 mtd->menucb(mtd->modedata, mtm->c, key);
921
922out:
923 mode_tree_remove_ref(mtd);
924 free(mtm);
925}
926
927static void
928mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
929 u_int y, int outside)
930{
931 struct mode_tree_item *mti;
932 struct menu *menu;
933 const struct menu_item *items;
934 struct mode_tree_menu *mtm;
935 char *title;
936 u_int line;
937
938 if (mtd->offset + y > mtd->line_size - 1)
939 line = mtd->current;
940 else
941 line = mtd->offset + y;
942 mti = mtd->line_list[line].item;
943
944 if (!outside) {
945 items = mtd->menu;
946 xasprintf(&title, "#[align=centre]%s", mti->name);
947 } else {
948 items = mode_tree_menu_items;
949 title = xstrdup("");
950 }
951 menu = menu_create(title);
952 menu_add_items(menu, items, NULL((void *)0), c, NULL((void *)0));
953 free(title);
954
955 mtm = xmalloc(sizeof *mtm);
956 mtm->data = mtd;
957 mtm->c = c;
958 mtm->line = line;
959 mtd->references++;
960
961 if (x >= (menu->width + 4) / 2)
962 x -= (menu->width + 4) / 2;
963 else
964 x = 0;
965 if (menu_display(menu, 0, 0, NULL((void *)0), x, y, c, BOX_LINES_DEFAULT, NULL((void *)0),
966 NULL((void *)0), NULL((void *)0), NULL((void *)0), mode_tree_menu_callback, mtm) != 0)
967 menu_free(menu);
968}
969
970int
971mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
972 struct mouse_event *m, u_int *xp, u_int *yp)
973{
974 struct mode_tree_line *line;
975 struct mode_tree_item *current, *parent, *mti;
976 u_int i, x, y;
977 int choice;
978
979 if (KEYC_IS_MOUSE(*key)(((*key) & 0x000fffffffffffULL) >= KEYC_MOUSE &&
((*key) & 0x000fffffffffffULL) < KEYC_BSPACE)
&& m != NULL((void *)0)) {
980 if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
981 *key = KEYC_NONE0x000ff000000000ULL;
982 return (0);
983 }
984 if (xp != NULL((void *)0))
985 *xp = x;
986 if (yp != NULL((void *)0))
987 *yp = y;
988 if (x > mtd->width || y > mtd->height) {
989 if (*key == KEYC_MOUSEDOWN3_PANE)
990 mode_tree_display_menu(mtd, c, x, y, 1);
991 if (!mtd->preview)
992 *key = KEYC_NONE0x000ff000000000ULL;
993 return (0);
994 }
995 if (mtd->offset + y < mtd->line_size) {
996 if (*key == KEYC_MOUSEDOWN1_PANE ||
997 *key == KEYC_MOUSEDOWN3_PANE ||
998 *key == KEYC_DOUBLECLICK1_PANE)
999 mtd->current = mtd->offset + y;
1000 if (*key == KEYC_DOUBLECLICK1_PANE)
1001 *key = '\r';
1002 else {
1003 if (*key == KEYC_MOUSEDOWN3_PANE)
1004 mode_tree_display_menu(mtd, c, x, y, 0);
1005 *key = KEYC_NONE0x000ff000000000ULL;
1006 }
1007 } else {
1008 if (*key == KEYC_MOUSEDOWN3_PANE)
1009 mode_tree_display_menu(mtd, c, x, y, 0);
1010 *key = KEYC_NONE0x000ff000000000ULL;
1011 }
1012 return (0);
1013 }
1014
1015 line = &mtd->line_list[mtd->current];
1016 current = line->item;
1017
1018 choice = -1;
1019 for (i = 0; i < mtd->line_size; i++) {
1020 if (*key == mtd->line_list[i].item->key) {
1021 choice = i;
1022 break;
1023 }
1024 }
1025 if (choice != -1) {
1026 if ((u_int)choice > mtd->line_size - 1) {
1027 *key = KEYC_NONE0x000ff000000000ULL;
1028 return (0);
1029 }
1030 mtd->current = choice;
1031 *key = '\r';
1032 return (0);
1033 }
1034
1035 switch (*key) {
1036 case 'q':
1037 case '\033': /* Escape */
1038 case '\007': /* C-g */
1039 return (1);
1040 case KEYC_UP:
1041 case 'k':
1042 case KEYC_WHEELUP_PANE:
1043 case '\020': /* C-p */
1044 mode_tree_up(mtd, 1);
1045 break;
1046 case KEYC_DOWN:
1047 case 'j':
1048 case KEYC_WHEELDOWN_PANE:
1049 case '\016': /* C-n */
1050 mode_tree_down(mtd, 1);
1051 break;
1052 case KEYC_PPAGE:
1053 case '\002': /* C-b */
1054 for (i = 0; i < mtd->height; i++) {
1055 if (mtd->current == 0)
1056 break;
1057 mode_tree_up(mtd, 1);
1058 }
1059 break;
1060 case KEYC_NPAGE:
1061 case '\006': /* C-f */
1062 for (i = 0; i < mtd->height; i++) {
1063 if (mtd->current == mtd->line_size - 1)
1064 break;
1065 mode_tree_down(mtd, 1);
1066 }
1067 break;
1068 case 'g':
1069 case KEYC_HOME:
1070 mtd->current = 0;
1071 mtd->offset = 0;
1072 break;
1073 case 'G':
1074 case KEYC_END:
1075 mtd->current = mtd->line_size - 1;
1076 if (mtd->current > mtd->height - 1)
1077 mtd->offset = mtd->current - mtd->height + 1;
1078 else
1079 mtd->offset = 0;
1080 break;
1081 case 't':
1082 /*
1083 * Do not allow parents and children to both be tagged: untag
1084 * all parents and children of current.
1085 */
1086 if (current->no_tag)
1087 break;
1088 if (!current->tagged) {
1089 parent = current->parent;
1090 while (parent != NULL((void *)0)) {
1091 parent->tagged = 0;
1092 parent = parent->parent;
1093 }
1094 mode_tree_clear_tagged(&current->children);
1095 current->tagged = 1;
1096 } else
1097 current->tagged = 0;
1098 if (m != NULL((void *)0))
1099 mode_tree_down(mtd, 0);
1100 break;
1101 case 'T':
1102 for (i = 0; i < mtd->line_size; i++)
1103 mtd->line_list[i].item->tagged = 0;
1104 break;
1105 case '\024': /* C-t */
1106 for (i = 0; i < mtd->line_size; i++) {
1107 if ((mtd->line_list[i].item->parent == NULL((void *)0) &&
1108 !mtd->line_list[i].item->no_tag) ||
1109 (mtd->line_list[i].item->parent != NULL((void *)0) &&
1110 mtd->line_list[i].item->parent->no_tag))
1111 mtd->line_list[i].item->tagged = 1;
1112 else
1113 mtd->line_list[i].item->tagged = 0;
1114 }
1115 break;
1116 case 'O':
1117 mtd->sort_crit.field++;
1118 if (mtd->sort_crit.field >= mtd->sort_size)
1119 mtd->sort_crit.field = 0;
1120 mode_tree_build(mtd);
1121 break;
1122 case 'r':
1123 mtd->sort_crit.reversed = !mtd->sort_crit.reversed;
1124 mode_tree_build(mtd);
1125 break;
1126 case KEYC_LEFT:
1127 case 'h':
1128 case '-':
1129 if (line->flat || !current->expanded)
1130 current = current->parent;
1131 if (current == NULL((void *)0))
1132 mode_tree_up(mtd, 0);
1133 else {
1134 current->expanded = 0;
1135 mtd->current = current->line;
1136 mode_tree_build(mtd);
1137 }
1138 break;
1139 case KEYC_RIGHT:
1140 case 'l':
1141 case '+':
1142 if (line->flat || current->expanded)
1143 mode_tree_down(mtd, 0);
1144 else if (!line->flat) {
1145 current->expanded = 1;
1146 mode_tree_build(mtd);
1147 }
1148 break;
1149 case '-'|KEYC_META0x00100000000000ULL:
1150 TAILQ_FOREACH(mti, &mtd->children, entry)for((mti) = ((&mtd->children)->tqh_first); (mti) !=
((void *)0); (mti) = ((mti)->entry.tqe_next))
1151 mti->expanded = 0;
1152 mode_tree_build(mtd);
1153 break;
1154 case '+'|KEYC_META0x00100000000000ULL:
1155 TAILQ_FOREACH(mti, &mtd->children, entry)for((mti) = ((&mtd->children)->tqh_first); (mti) !=
((void *)0); (mti) = ((mti)->entry.tqe_next))
1156 mti->expanded = 1;
1157 mode_tree_build(mtd);
1158 break;
1159 case '?':
1160 case '/':
1161 case '\023': /* C-s */
1162 mtd->references++;
1163 status_prompt_set(c, NULL((void *)0), "(search) ", "",
1164 mode_tree_search_callback, mode_tree_search_free, mtd,
1165 PROMPT_NOFORMAT0x8, PROMPT_TYPE_SEARCH);
1166 break;
1167 case 'n':
1168 mode_tree_search_set(mtd);
1169 break;
1170 case 'f':
1171 mtd->references++;
1172 status_prompt_set(c, NULL((void *)0), "(filter) ", mtd->filter,
1173 mode_tree_filter_callback, mode_tree_filter_free, mtd,
1174 PROMPT_NOFORMAT0x8, PROMPT_TYPE_SEARCH);
1175 break;
1176 case 'v':
1177 mtd->preview = !mtd->preview;
1178 mode_tree_build(mtd);
1179 if (mtd->preview)
1180 mode_tree_check_selected(mtd);
1181 break;
1182 }
1183 return (0);
1184}
1185
1186void
1187mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
1188 const char *template, const char *name)
1189{
1190 struct cmdq_state *state;
1191 char *command, *error;
1192 enum cmd_parse_status status;
1193
1194 command = cmd_template_replace(template, name, 1);
1195 if (command != NULL((void *)0) && *command != '\0') {
1196 state = cmdq_new_state(fs, NULL((void *)0), 0);
1197 status = cmd_parse_and_append(command, NULL((void *)0), c, state, &error);
1198 if (status == CMD_PARSE_ERROR) {
1199 if (c != NULL((void *)0)) {
1200 *error = toupper((u_char)*error);
1201 status_message_set(c, -1, 1, 0, "%s", error);
1202 }
1203 free(error);
1204 }
1205 cmdq_free_state(state);
1206 }
1207 free(command);
1208}