Coverage Report

Created: 2026-06-12 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/sort.c
Line
Count
Source
1
/* $OpenBSD$ */
2
3
/*
4
 * Copyright (c) 2026 Dane Jensen <dhcjensen@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
static struct sort_criteria *sort_criteria;
27
28
static void
29
sort_qsort(void *l, u_int len, u_int size, int (*cmp)(const void *, const void *),
30
    struct sort_criteria *sort_crit)
31
10.9k
{
32
10.9k
  u_int  i;
33
10.9k
  void  *tmp, **ll;
34
35
10.9k
  if (sort_crit->order == SORT_END)
36
0
    return;
37
38
10.9k
  if (sort_crit->order == SORT_ORDER) {
39
1.43k
    if (sort_crit->reversed) {
40
516
      ll = l;
41
516
      for (i = 0; i < len / 2; i++) {
42
0
        tmp = ll[i];
43
0
        ll[i] = ll[len - 1 - i];
44
0
        ll[len - 1 - i] = tmp;
45
0
      }
46
516
    }
47
9.49k
  } else {
48
9.49k
    sort_criteria = sort_crit;
49
9.49k
    qsort(l, len, size, cmp);
50
9.49k
  }
51
10.9k
}
52
53
static int
54
sort_buffer_cmp(const void *a0, const void *b0)
55
0
{
56
0
  struct sort_criteria      *sort_crit = sort_criteria;
57
0
  const struct paste_buffer *const  *a = a0;
58
0
  const struct paste_buffer *const  *b = b0;
59
0
  const struct paste_buffer   *pa = *a;
60
0
  const struct paste_buffer   *pb = *b;
61
0
  int          result = 0;
62
63
0
  switch (sort_crit->order) {
64
0
  case SORT_NAME:
65
0
    result = strcmp(pa->name, pb->name);
66
0
    break;
67
0
  case SORT_CREATION:
68
0
    if (pa->order > pb->order)
69
0
      result = -1;
70
0
    else if (pa->order < pb->order)
71
0
      result = 1;
72
0
    else
73
0
      result = 0;
74
0
    break;
75
0
  case SORT_SIZE:
76
0
    result = pa->size - pb->size;
77
0
    break;
78
0
  case SORT_ACTIVITY:
79
0
  case SORT_INDEX:
80
0
  case SORT_MODIFIER:
81
0
  case SORT_ORDER:
82
0
  case SORT_Z:
83
0
  case SORT_END:
84
0
    break;
85
0
  }
86
87
0
  if (result == 0)
88
0
    result = strcmp(pa->name, pb->name);
89
90
0
  if (sort_crit->reversed)
91
0
    result = -result;
92
0
  return (result);
93
0
}
94
95
static int
96
sort_client_cmp(const void *a0, const void *b0)
97
0
{
98
0
  struct sort_criteria    *sort_crit = sort_criteria;
99
0
  const struct client *const  *a = a0;
100
0
  const struct client *const  *b = b0;
101
0
  const struct client   *ca = *a;
102
0
  const struct client   *cb = *b;
103
0
  int        result = 0;
104
105
0
  switch (sort_crit->order) {
106
0
  case SORT_NAME:
107
0
    result = strcmp(ca->name, cb->name);
108
0
    break;
109
0
  case SORT_SIZE:
110
0
    result = ca->tty.sx - cb->tty.sx;
111
0
    if (result == 0)
112
0
      result = ca->tty.sy - cb->tty.sy;
113
0
    break;
114
0
  case SORT_CREATION:
115
0
    if (timercmp(&ca->creation_time, &cb->creation_time, >))
116
0
      result = 1;
117
0
    else if (timercmp(&ca->creation_time, &cb->creation_time, <))
118
0
      result = -1;
119
0
    break;
120
0
  case SORT_ACTIVITY:
121
0
    if (timercmp(&ca->activity_time, &cb->activity_time, >))
122
0
      result = -1;
123
0
    else if (timercmp(&ca->activity_time, &cb->activity_time, <))
124
0
      result = 1;
125
0
    break;
126
0
  case SORT_INDEX:
127
0
  case SORT_MODIFIER:
128
0
  case SORT_ORDER:
129
0
  case SORT_Z:
130
0
  case SORT_END:
131
0
    break;
132
0
  }
133
134
0
  if (result == 0)
135
0
    result = strcmp(ca->name, cb->name);
136
137
0
  if (sort_crit->reversed)
138
0
    result = -result;
139
0
  return (result);
140
0
}
141
142
static int
143
sort_session_cmp(const void *a0, const void *b0)
144
0
{
145
0
  struct sort_criteria    *sort_crit = sort_criteria;
146
0
  const struct session *const *a = a0;
147
0
  const struct session *const *b = b0;
148
0
  const struct session    *sa = *a;
149
0
  const struct session    *sb = *b;
150
0
  int        result = 0;
151
152
0
  switch (sort_crit->order) {
153
0
  case SORT_INDEX:
154
0
    result = sa->id - sb->id;
155
0
    break;
156
0
  case SORT_CREATION:
157
0
    if (timercmp(&sa->creation_time, &sb->creation_time, >)) {
158
0
      result = 1;
159
0
      break;
160
0
    }
161
0
    if (timercmp(&sa->creation_time, &sb->creation_time, <)) {
162
0
      result = -1;
163
0
      break;
164
0
    }
165
0
    break;
166
0
  case SORT_ACTIVITY:
167
0
    if (timercmp(&sa->activity_time, &sb->activity_time, >)) {
168
0
      result = -1;
169
0
      break;
170
0
    }
171
0
    if (timercmp(&sa->activity_time, &sb->activity_time, <)) {
172
0
      result = 1;
173
0
      break;
174
0
    }
175
0
    break;
176
0
  case SORT_NAME:
177
0
    result = strcmp(sa->name, sb->name);
178
0
    break;
179
0
  case SORT_MODIFIER:
180
0
  case SORT_ORDER:
181
0
  case SORT_SIZE:
182
0
  case SORT_Z:
183
0
  case SORT_END:
184
0
    break;
185
0
  }
186
187
0
  if (result == 0)
188
0
    result = strcmp(sa->name, sb->name);
189
190
0
  if (sort_crit->reversed)
191
0
    result = -result;
192
0
  return (result);
193
0
}
194
195
static int
196
sort_pane_cmp(const void *a0, const void *b0)
197
0
{
198
0
  struct sort_criteria  *sort_crit = sort_criteria;
199
0
  struct window_pane  *a = *(struct window_pane **)a0;
200
0
  struct window_pane  *b = *(struct window_pane **)b0;
201
0
  int      result = 0;
202
0
  u_int      ai, bi;
203
204
0
  switch (sort_crit->order) {
205
0
  case SORT_ACTIVITY:
206
0
    result = a->active_point - b->active_point;
207
0
    break;
208
0
  case SORT_CREATION:
209
0
    result = a->id - b->id;
210
0
    break;
211
0
  case SORT_SIZE:
212
0
    result = a->sx * a->sy - b->sx * b->sy;
213
0
    break;
214
0
  case SORT_INDEX:
215
0
    window_pane_index(a, &ai);
216
0
    window_pane_index(b, &bi);
217
0
    result = ai - bi;
218
0
    break;
219
0
  case SORT_NAME:
220
0
    result = strcmp(a->screen->title, b->screen->title);
221
0
    break;
222
0
  case SORT_Z:
223
0
    window_pane_zindex(a, &ai);
224
0
    window_pane_zindex(b, &bi);
225
0
    result = ai - bi;
226
0
    break;
227
0
  case SORT_MODIFIER:
228
0
  case SORT_ORDER:
229
0
  case SORT_END:
230
0
    break;
231
0
  }
232
233
0
  if (result == 0)
234
0
    result = strcmp(a->screen->title, b->screen->title);
235
236
0
  if (sort_crit->reversed)
237
0
    result = -result;
238
0
  return (result);
239
0
}
240
241
static int
242
sort_winlink_cmp(const void *a0, const void *b0)
243
0
{
244
0
  struct sort_criteria    *sort_crit = sort_criteria;
245
0
  const struct winlink *const *a = a0;
246
0
  const struct winlink *const *b = b0;
247
0
  const struct winlink    *wla = *a;
248
0
  const struct winlink    *wlb = *b;
249
0
  struct window     *wa = wla->window;
250
0
  struct window     *wb = wlb->window;
251
0
  int        result = 0;
252
253
0
  switch (sort_crit->order) {
254
0
  case SORT_INDEX:
255
0
    result = wla->idx - wlb->idx;
256
0
    break;
257
0
  case SORT_CREATION:
258
0
    if (timercmp(&wa->creation_time, &wb->creation_time, >)) {
259
0
      result = 1;
260
0
      break;
261
0
    }
262
0
    if (timercmp(&wa->creation_time, &wb->creation_time, <)) {
263
0
      result = -1;
264
0
      break;
265
0
    }
266
0
    break;
267
0
  case SORT_ACTIVITY:
268
0
    if (timercmp(&wa->activity_time, &wb->activity_time, >)) {
269
0
      result = -1;
270
0
      break;
271
0
    }
272
0
    if (timercmp(&wa->activity_time, &wb->activity_time, <)) {
273
0
      result = 1;
274
0
      break;
275
0
    }
276
0
    break;
277
0
  case SORT_NAME:
278
0
    result = strcmp(wa->name, wb->name);
279
0
    break;
280
0
  case SORT_SIZE:
281
0
    result = wa->sx * wa->sy - wb->sx * wb->sy;
282
0
    break;
283
0
  case SORT_MODIFIER:
284
0
  case SORT_ORDER:
285
0
  case SORT_Z:
286
0
  case SORT_END:
287
0
    break;
288
0
  }
289
290
0
  if (result == 0)
291
0
    result = strcmp(wa->name, wb->name);
292
293
0
  if (sort_crit->reversed)
294
0
    result = -result;
295
0
  return (result);
296
0
}
297
298
static int
299
sort_key_binding_cmp(const void *a0, const void *b0)
300
0
{
301
0
  struct sort_criteria    *sort_crit = sort_criteria;
302
0
  const struct key_binding  *a = *(struct key_binding **)a0;
303
0
  const struct key_binding  *b = *(struct key_binding **)b0;
304
0
  int        result = 0;
305
306
0
  switch (sort_crit->order) {
307
0
  case SORT_INDEX:
308
0
    result = a->key - b->key;
309
0
    break;
310
0
  case SORT_MODIFIER:
311
0
    result = (a->key & KEYC_MASK_MODIFIERS) -
312
0
        (b->key & KEYC_MASK_MODIFIERS);
313
0
    break;
314
0
  case SORT_NAME:
315
0
    result = strcasecmp(a->tablename, b->tablename) == 0;
316
0
    break;
317
0
  case SORT_ACTIVITY:
318
0
  case SORT_CREATION:
319
0
  case SORT_ORDER:
320
0
  case SORT_SIZE:
321
0
  case SORT_Z:
322
0
  case SORT_END:
323
0
    break;
324
0
  }
325
326
0
  if (result == 0)
327
0
    result = strcasecmp(a->tablename, b->tablename) == 0;
328
329
0
  if (sort_crit->reversed)
330
0
    result = -result;
331
0
  return (result);
332
0
}
333
334
void
335
sort_next_order(struct sort_criteria *sort_crit)
336
0
{
337
0
  u_int i;
338
339
0
  if (sort_crit->order_seq == NULL)
340
0
    return;
341
0
  for (i = 0; sort_crit->order_seq[i] != SORT_END; i++) {
342
0
    if (sort_crit->order == sort_crit->order_seq[i])
343
0
      break;
344
0
  }
345
346
0
  if (sort_crit->order_seq[i] == SORT_END)
347
0
    i = 0;
348
0
  else {
349
0
    i++;
350
0
    if (sort_crit->order_seq[i] == SORT_END)
351
0
      i = 0;
352
0
  }
353
0
  sort_crit->order = sort_crit->order_seq[i];
354
0
}
355
356
enum sort_order
357
sort_order_from_string(const char* order)
358
0
{
359
0
  if (order != NULL) {
360
0
    if (strcasecmp(order, "activity") == 0)
361
0
      return (SORT_ACTIVITY);
362
0
    if (strcasecmp(order, "creation") == 0)
363
0
      return (SORT_CREATION);
364
0
    if (strcasecmp(order, "index") == 0 ||
365
0
        strcasecmp(order, "key") == 0)
366
0
      return (SORT_INDEX);
367
0
    if (strcasecmp(order, "modifier") == 0)
368
0
      return (SORT_MODIFIER);
369
0
    if (strcasecmp(order, "name") == 0 ||
370
0
        strcasecmp(order, "title") == 0)
371
0
      return (SORT_NAME);
372
0
    if (strcasecmp(order, "order") == 0)
373
0
      return (SORT_ORDER);
374
0
    if (strcasecmp(order, "size") == 0)
375
0
      return (SORT_SIZE);
376
0
    if (strcasecmp(order, "z") == 0)
377
0
      return (SORT_Z);
378
0
  }
379
0
  return (SORT_END);
380
0
}
381
382
const char *
383
sort_order_to_string(enum sort_order order)
384
0
{
385
0
  if (order == SORT_ACTIVITY)
386
0
    return "activity";
387
0
  if (order == SORT_CREATION)
388
0
    return "creation";
389
0
  if (order == SORT_INDEX)
390
0
    return "index";
391
0
  if (order == SORT_MODIFIER)
392
0
    return "modifier";
393
0
  if (order == SORT_NAME)
394
0
    return "name";
395
0
  if (order == SORT_ORDER)
396
0
    return "order";
397
0
  if (order == SORT_SIZE)
398
0
    return "size";
399
0
  if (order == SORT_Z)
400
0
    return "z";
401
0
  return (NULL);
402
0
}
403
404
int
405
sort_would_window_tree_swap(struct sort_criteria *sort_crit,
406
    struct winlink *wla, struct winlink *wlb)
407
0
{
408
0
  if (sort_crit->order == SORT_INDEX)
409
0
    return (0);
410
0
  sort_criteria = sort_crit;
411
0
  return (sort_winlink_cmp(&wla, &wlb) != 0);
412
0
}
413
414
struct paste_buffer **
415
sort_get_buffers(u_int *n, struct sort_criteria *sort_crit)
416
0
{
417
0
  struct paste_buffer    *pb = NULL;
418
0
  u_int         i;
419
0
  static struct paste_buffer  **l = NULL;
420
0
  static u_int        lsz = 0;
421
422
0
  i = 0;
423
0
  while ((pb = paste_walk(pb)) != NULL) {
424
0
    if (lsz <= i) {
425
0
      lsz += 100;
426
0
      l = xreallocarray(l, lsz, sizeof *l);
427
0
    }
428
0
    l[i++] = pb;
429
0
  }
430
431
0
  sort_qsort(l, i, sizeof *l, sort_buffer_cmp, sort_crit);
432
0
  *n = i;
433
434
0
  return (l);
435
0
}
436
437
struct client **
438
sort_get_clients(u_int *n, struct sort_criteria *sort_crit)
439
1.29k
{
440
1.29k
  struct client    *c;
441
1.29k
  u_int       i;
442
1.29k
  static struct client  **l = NULL;
443
1.29k
  static u_int      lsz = 0;
444
445
1.29k
  i = 0;
446
1.29k
  TAILQ_FOREACH(c, &clients, entry) {
447
0
    if (c->flags & CLIENT_UNATTACHEDFLAGS)
448
0
      continue;
449
0
    if (~c->flags & CLIENT_ATTACHED)
450
0
      continue;
451
0
    if (lsz <= i) {
452
0
      lsz += 100;
453
0
      l = xreallocarray(l, lsz, sizeof *l);
454
0
    }
455
0
    l[i++] = c;
456
0
  }
457
458
1.29k
  sort_qsort(l, i, sizeof *l, sort_client_cmp, sort_crit);
459
1.29k
  *n = i;
460
461
1.29k
  return (l);
462
1.29k
}
463
464
struct session **
465
sort_get_sessions(u_int *n, struct sort_criteria *sort_crit)
466
9.62k
{
467
9.62k
  struct session     *s;
468
9.62k
  u_int       i;
469
9.62k
  static struct session **l = NULL;
470
9.62k
  static u_int      lsz = 0;
471
472
9.62k
  i = 0;
473
9.62k
  RB_FOREACH(s, sessions, &sessions) {
474
0
    if (lsz <= i) {
475
0
      lsz += 100;
476
0
      l = xreallocarray(l, lsz, sizeof *l);
477
0
    }
478
0
    l[i++] = s;
479
0
  }
480
481
9.62k
  sort_qsort(l, i, sizeof *l, sort_session_cmp, sort_crit);
482
9.62k
  *n = i;
483
484
9.62k
  return (l);
485
9.62k
}
486
487
struct window_pane **
488
sort_get_panes(u_int *n, struct sort_criteria *sort_crit)
489
0
{
490
0
  struct session       *s;
491
0
  struct winlink       *wl;
492
0
  struct window      *w;
493
0
  struct window_pane     *wp;
494
0
  u_int         i;
495
0
  static struct window_pane **l = NULL;
496
0
  static u_int        lsz = 0;
497
498
0
  i = 0;
499
0
  RB_FOREACH(s, sessions, &sessions) {
500
0
    RB_FOREACH(wl, winlinks, &s->windows)  {
501
0
      w = wl->window;
502
0
      TAILQ_FOREACH(wp, &w->panes, entry) {
503
0
        if (lsz <= i) {
504
0
          lsz += 100;
505
0
          l = xreallocarray(l, lsz, sizeof *l);
506
0
        }
507
0
        l[i++] = wp;
508
0
      }
509
0
    }
510
0
  }
511
512
0
  sort_qsort(l, i, sizeof *l, sort_pane_cmp, sort_crit);
513
0
  *n = i;
514
515
0
  return (l);
516
0
}
517
518
struct window_pane **
519
sort_get_panes_session(struct session *s, u_int *n,
520
    struct sort_criteria *sort_crit)
521
0
{
522
0
  struct winlink       *wl = NULL;
523
0
  struct window      *w = NULL;
524
0
  struct window_pane     *wp = NULL;
525
0
  u_int         i;
526
0
  static struct window_pane **l = NULL;
527
0
  static u_int        lsz = 0;
528
529
0
  i = 0;
530
0
  RB_FOREACH(wl, winlinks, &s->windows)  {
531
0
    w = wl->window;
532
0
    TAILQ_FOREACH(wp, &w->panes, entry) {
533
0
      if (lsz <= i) {
534
0
        lsz += 100;
535
0
        l = xreallocarray(l, lsz, sizeof *l);
536
0
      }
537
0
      l[i++] = wp;
538
0
    }
539
0
  }
540
541
0
  sort_qsort(l, i, sizeof *l, sort_pane_cmp, sort_crit);
542
0
  *n = i;
543
544
0
  return (l);
545
0
}
546
547
struct window_pane **
548
sort_get_panes_window(struct window *w, u_int *n,
549
    struct sort_criteria *sort_crit)
550
0
{
551
0
  struct window_pane     *wp;
552
0
  u_int         i;
553
0
  static struct window_pane **l = NULL;
554
0
  static u_int        lsz = 0;
555
556
0
  i = 0;
557
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
558
0
    if (lsz <= i) {
559
0
      lsz += 100;
560
0
      l = xreallocarray(l, lsz, sizeof *l);
561
0
    }
562
0
    l[i++] = wp;
563
0
  }
564
565
0
  sort_qsort(l, i, sizeof *l, sort_pane_cmp, sort_crit);
566
0
  *n = i;
567
568
0
  return (l);
569
0
}
570
571
struct winlink **
572
sort_get_winlinks(u_int *n, struct sort_criteria *sort_crit)
573
0
{
574
0
  struct session     *s;
575
0
  struct winlink     *wl;
576
0
  u_int       i;
577
0
  static struct winlink **l = NULL;
578
0
  static u_int      lsz = 0;
579
580
0
  i = 0;
581
0
  RB_FOREACH(s, sessions, &sessions) {
582
0
    RB_FOREACH(wl, winlinks, &s->windows) {
583
0
      if (lsz <= i) {
584
0
        lsz += 100;
585
0
        l = xreallocarray(l, lsz, sizeof *l);
586
0
      }
587
0
      l[i++] = wl;
588
0
    }
589
0
  }
590
591
0
  sort_qsort(l, i, sizeof *l, sort_winlink_cmp, sort_crit);
592
0
  *n = i;
593
594
0
  return (l);
595
0
}
596
597
struct winlink **
598
sort_get_winlinks_session(struct session *s, u_int *n,
599
    struct sort_criteria *sort_crit)
600
0
{
601
0
  struct winlink     *wl;
602
0
  u_int       i;
603
0
  static struct winlink **l = NULL;
604
0
  static u_int      lsz = 0;
605
606
0
  i = 0;
607
0
  RB_FOREACH(wl, winlinks, &s->windows) {
608
0
    if (lsz <= i) {
609
0
      lsz += 100;
610
0
      l = xreallocarray(l, lsz, sizeof *l);
611
0
    }
612
0
    l[i++] = wl;
613
0
  }
614
615
0
  sort_qsort(l, i, sizeof *l, sort_winlink_cmp, sort_crit);
616
0
  *n = i;
617
618
0
  return (l);
619
0
}
620
621
struct key_binding **
622
sort_get_key_bindings(u_int *n, struct sort_criteria *sort_crit)
623
0
{
624
0
  struct key_table     *table;
625
0
  struct key_binding     *bd;
626
0
  u_int         i = 0;
627
0
  static struct key_binding **l = NULL;
628
0
  static u_int        lsz = 0;
629
630
0
  table = key_bindings_first_table();
631
0
  while (table != NULL) {
632
0
    bd = key_bindings_first(table);
633
0
    while (bd != NULL) {
634
0
      if (lsz <= i) {
635
0
        lsz += 100;
636
0
        l = xreallocarray(l, lsz, sizeof *l);
637
0
      }
638
0
      l[i++] = bd;
639
0
      bd = key_bindings_next(table, bd);
640
0
    }
641
0
    table = key_bindings_next_table(table);
642
0
  }
643
644
0
  sort_qsort(l, i, sizeof *l, sort_key_binding_cmp, sort_crit);
645
0
  *n = i;
646
647
0
  return (l);
648
0
}
649
650
struct key_binding **
651
sort_get_key_bindings_table(struct key_table *table, u_int *n,
652
    struct sort_criteria *sort_crit)
653
0
{
654
0
  struct key_binding     *bd;
655
0
  u_int         i = 0;
656
0
  static struct key_binding **l = NULL;
657
0
  static u_int        lsz = 0;
658
659
0
  bd = key_bindings_first(table);
660
0
  while (bd != NULL) {
661
0
    if (lsz <= i) {
662
0
      lsz += 100;
663
0
      l = xreallocarray(l, lsz, sizeof *l);
664
0
    }
665
0
    l[i++] = bd;
666
0
    bd = key_bindings_next(table, bd);
667
0
  }
668
669
0
  sort_qsort(l, i, sizeof *l, sort_key_binding_cmp, sort_crit);
670
0
  *n = i;
671
672
0
  return (l);
673
0
}