Coverage Report

Created: 2026-05-30 06:39

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