/* $OpenBSD$ */ /* * Copyright (c) 2026 Dane Jensen * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" static struct sort_criteria *sort_criteria; static void sort_qsort(void *l, u_int len, u_int size, int (*cmp)(const void *, const void *), struct sort_criteria *sort_crit) { u_int i; void *tmp, **ll; if (sort_crit->order == SORT_END) return; if (sort_crit->order == SORT_ORDER) { if (sort_crit->reversed) { ll = l; for (i = 0; i < len / 2; i++) { tmp = ll[i]; ll[i] = ll[len - 1 - i]; ll[len - 1 - i] = tmp; } } } else { sort_criteria = sort_crit; qsort(l, len, size, cmp); } } static int sort_buffer_cmp(const void *a0, const void *b0) { struct sort_criteria *sort_crit = sort_criteria; const struct paste_buffer *const *a = a0; const struct paste_buffer *const *b = b0; const struct paste_buffer *pa = *a; const struct paste_buffer *pb = *b; int result = 0; switch (sort_crit->order) { case SORT_NAME: result = strcmp(pa->name, pb->name); break; case SORT_CREATION: result = pa->order - pb->order; break; case SORT_SIZE: result = pa->size - pb->size; break; case SORT_ACTIVITY: case SORT_INDEX: case SORT_ORDER: case SORT_END: break; } if (result == 0) result = strcmp(pa->name, pb->name); if (sort_crit->reversed) result = -result; return (result); } static int sort_client_cmp(const void *a0, const void *b0) { struct sort_criteria *sort_crit = sort_criteria; const struct client *const *a = a0; const struct client *const *b = b0; const struct client *ca = *a; const struct client *cb = *b; int result = 0; switch (sort_crit->order) { case SORT_NAME: result = strcmp(ca->name, cb->name); break; case SORT_SIZE: result = ca->tty.sx - cb->tty.sx; if (result == 0) result = ca->tty.sy - cb->tty.sy; break; case SORT_CREATION: if (timercmp(&ca->creation_time, &cb->creation_time, >)) result = 1; else if (timercmp(&ca->creation_time, &cb->creation_time, <)) result = -1; break; case SORT_ACTIVITY: if (timercmp(&ca->activity_time, &cb->activity_time, >)) result = -1; else if (timercmp(&ca->activity_time, &cb->activity_time, <)) result = 1; break; case SORT_INDEX: case SORT_ORDER: case SORT_END: break; } if (result == 0) result = strcmp(ca->name, cb->name); if (sort_crit->reversed) result = -result; return (result); } static int sort_session_cmp(const void *a0, const void *b0) { struct sort_criteria *sort_crit = sort_criteria; const struct session *const *a = a0; const struct session *const *b = b0; const struct session *sa = *a; const struct session *sb = *b; int result = 0; switch (sort_crit->order) { case SORT_INDEX: result = sa->id - sb->id; break; case SORT_CREATION: if (timercmp(&sa->creation_time, &sb->creation_time, >)) { result = 1; break; } if (timercmp(&sa->creation_time, &sb->creation_time, <)) { result = -1; break; } break; case SORT_ACTIVITY: if (timercmp(&sa->activity_time, &sb->activity_time, >)) { result = -1; break; } if (timercmp(&sa->activity_time, &sb->activity_time, <)) { result = 1; break; } break; case SORT_NAME: result = strcmp(sa->name, sb->name); break; case SORT_ORDER: case SORT_SIZE: case SORT_END: break; } if (result == 0) result = strcmp(sa->name, sb->name); if (sort_crit->reversed) result = -result; return (result); } static int sort_pane_cmp(const void *a0, const void *b0) { struct sort_criteria *sort_crit = sort_criteria; struct window_pane *a = *(struct window_pane **)a0; struct window_pane *b = *(struct window_pane **)b0; int result = 0; u_int ai, bi; switch (sort_crit->order) { case SORT_ACTIVITY: result = a->active_point - b->active_point; break; case SORT_CREATION: result = a->id - b->id; break; case SORT_INDEX: case SORT_NAME: case SORT_ORDER: case SORT_SIZE: case SORT_END: break; } if (result == 0) { /* * Panes don't have names, so use number order for any other * sort field. */ window_pane_index(a, &ai); window_pane_index(b, &bi); result = ai - bi; } if (sort_crit->reversed) result = -result; return (result); } static int sort_winlink_cmp(const void *a0, const void *b0) { struct sort_criteria *sort_crit = sort_criteria; const struct winlink *const *a = a0; const struct winlink *const *b = b0; const struct winlink *wla = *a; const struct winlink *wlb = *b; struct window *wa = wla->window; struct window *wb = wlb->window; int result = 0; switch (sort_crit->order) { case SORT_INDEX: result = wla->idx - wlb->idx; break; case SORT_ACTIVITY: if (timercmp(&wa->activity_time, &wb->activity_time, >)) { result = -1; break; } if (timercmp(&wa->activity_time, &wb->activity_time, <)) { result = 1; break; } break; case SORT_NAME: result = strcmp(wa->name, wb->name); break; case SORT_CREATION: case SORT_ORDER: case SORT_SIZE: case SORT_END: break; } if (result == 0) result = strcmp(wa->name, wb->name); if (sort_crit->reversed) result = -result; return (result); } void sort_next_order(struct sort_criteria *sort_crit) { u_int i; if (sort_crit->order_seq == NULL) return; for (i = 0; sort_crit->order_seq[i] != SORT_END; i++) { if (sort_crit->order == sort_crit->order_seq[i]) break; } if (sort_crit->order_seq[i] == SORT_END) i = 0; else { i++; if (sort_crit->order_seq[i] == SORT_END) i = 0; } sort_crit->order = sort_crit->order_seq[i]; } enum sort_order sort_order_from_string(const char* order) { if (order != NULL) { if (strcasecmp(order, "activity") == 0) return (SORT_ACTIVITY); if (strcasecmp(order, "creation") == 0) return (SORT_CREATION); if (strcasecmp(order, "index") == 0) return (SORT_INDEX); if (strcasecmp(order, "name") == 0) return (SORT_NAME); if (strcasecmp(order, "order") == 0) return (SORT_ORDER); if (strcasecmp(order, "size") == 0) return (SORT_SIZE); } return (SORT_END); } const char * sort_order_to_string(enum sort_order order) { if (order == SORT_ACTIVITY) return "activity"; if (order == SORT_CREATION) return "creation"; if (order == SORT_INDEX) return "index"; if (order == SORT_NAME) return "name"; if (order == SORT_ORDER) return "order"; if (order == SORT_SIZE) return "size"; return (NULL); } int sort_would_window_tree_swap(struct sort_criteria *sort_crit, struct winlink *wla, struct winlink *wlb) { if (sort_crit->order == SORT_INDEX) return (0); sort_criteria = sort_crit; return (sort_winlink_cmp(&wla, &wlb) != 0); } struct paste_buffer ** sort_get_buffers(u_int *n, struct sort_criteria *sort_crit) { struct paste_buffer *pb = NULL; u_int i; static struct paste_buffer **l = NULL; static u_int lsz = 0; i = 0; while ((pb = paste_walk(pb)) != NULL) { if (lsz <= i) { lsz += 100; l = xreallocarray(l, lsz, sizeof *l); } l[i++] = pb; } sort_qsort(l, i, sizeof *l, sort_buffer_cmp, sort_crit); *n = i; return (l); } struct client ** sort_get_clients(u_int *n, struct sort_criteria *sort_crit) { struct client *c; u_int i; static struct client **l = NULL; static u_int lsz = 0; i = 0; TAILQ_FOREACH(c, &clients, entry) { if (lsz <= i) { lsz += 100; l = xreallocarray(l, lsz, sizeof *l); } l[i++] = c; } sort_qsort(l, i, sizeof *l, sort_client_cmp, sort_crit); *n = i; return (l); } struct session ** sort_get_sessions(u_int *n, struct sort_criteria *sort_crit) { struct session *s; u_int i; static struct session **l = NULL; static u_int lsz = 0; i = 0; RB_FOREACH(s, sessions, &sessions) { if (lsz <= i) { lsz += 100; l = xreallocarray(l, lsz, sizeof *l); } l[i++] = s; } sort_qsort(l, i, sizeof *l, sort_session_cmp, sort_crit); *n = i; return (l); } struct window_pane ** sort_get_panes(u_int *n, struct sort_criteria *sort_crit) { struct session *s; struct winlink *wl; struct window *w; struct window_pane *wp; u_int i; static struct window_pane **l = NULL; static u_int lsz = 0; i = 0; RB_FOREACH(s, sessions, &sessions) { RB_FOREACH(wl, winlinks, &s->windows) { w = wl->window; TAILQ_FOREACH(wp, &w->panes, entry) { if (lsz <= i) { lsz += 100; l = xreallocarray(l, lsz, sizeof *l); } l[i++] = wp; } } } sort_qsort(l, i, sizeof *l, sort_pane_cmp, sort_crit); *n = i; return (l); } struct window_pane ** sort_get_panes_session(struct session *s, u_int *n, struct sort_criteria *sort_crit) { struct winlink *wl = NULL; struct window *w = NULL; struct window_pane *wp = NULL; u_int i; static struct window_pane **l = NULL; static u_int lsz = 0; i = 0; RB_FOREACH(wl, winlinks, &s->windows) { TAILQ_FOREACH(wp, &w->panes, entry) { if (lsz <= i) { lsz += 100; l = xreallocarray(l, lsz, sizeof *l); } l[i++] = wp; } } sort_qsort(l, i, sizeof *l, sort_pane_cmp, sort_crit); *n = i; return (l); } struct window_pane ** sort_get_panes_window(struct window *w, u_int *n, struct sort_criteria *sort_crit) { struct window_pane *wp; u_int i; static struct window_pane **l = NULL; static u_int lsz = 0; i = 0; TAILQ_FOREACH(wp, &w->panes, entry) { if (lsz <= i) { lsz += 100; l = xreallocarray(l, lsz, sizeof *l); } l[i++] = wp; } sort_qsort(l, i, sizeof *l, sort_pane_cmp, sort_crit); *n = i; return (l); } struct winlink ** sort_get_winlinks(u_int *n, struct sort_criteria *sort_crit) { struct session *s; struct winlink *wl; u_int i; static struct winlink **l = NULL; static u_int lsz = 0; i = 0; RB_FOREACH(s, sessions, &sessions) { RB_FOREACH(wl, winlinks, &s->windows) { if (lsz <= i) { lsz += 100; l = xreallocarray(l, lsz, sizeof *l); } l[i++] = wl; } } sort_qsort(l, i, sizeof *l, sort_winlink_cmp, sort_crit); *n = i; return (l); } struct winlink ** sort_get_winlinks_session(struct session *s, u_int *n, struct sort_criteria *sort_crit) { struct winlink *wl; u_int i; static struct winlink **l = NULL; static u_int lsz = 0; i = 0; RB_FOREACH(wl, winlinks, &s->windows) { if (lsz <= i) { lsz += 100; l = xreallocarray(l, lsz, sizeof *l); } l[i++] = wl; } sort_qsort(l, i, sizeof *l, sort_winlink_cmp, sort_crit); *n = i; return (l); }