1*0a6a1f1dSLionel Sambuc /* Id */
2eda6f593SDavid van Moolenbroek
3eda6f593SDavid van Moolenbroek /*
4eda6f593SDavid van Moolenbroek * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
5eda6f593SDavid van Moolenbroek *
6eda6f593SDavid van Moolenbroek * Permission to use, copy, modify, and distribute this software for any
7eda6f593SDavid van Moolenbroek * purpose with or without fee is hereby granted, provided that the above
8eda6f593SDavid van Moolenbroek * copyright notice and this permission notice appear in all copies.
9eda6f593SDavid van Moolenbroek *
10eda6f593SDavid van Moolenbroek * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11eda6f593SDavid van Moolenbroek * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12eda6f593SDavid van Moolenbroek * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13eda6f593SDavid van Moolenbroek * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14eda6f593SDavid van Moolenbroek * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15eda6f593SDavid van Moolenbroek * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16eda6f593SDavid van Moolenbroek * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17eda6f593SDavid van Moolenbroek */
18eda6f593SDavid van Moolenbroek
19eda6f593SDavid van Moolenbroek #include <sys/types.h>
20eda6f593SDavid van Moolenbroek #include <sys/time.h>
21eda6f593SDavid van Moolenbroek
22eda6f593SDavid van Moolenbroek #include <errno.h>
23eda6f593SDavid van Moolenbroek #include <limits.h>
24eda6f593SDavid van Moolenbroek #include <stdarg.h>
25eda6f593SDavid van Moolenbroek #include <stdlib.h>
26eda6f593SDavid van Moolenbroek #include <string.h>
27eda6f593SDavid van Moolenbroek #include <time.h>
28eda6f593SDavid van Moolenbroek #include <unistd.h>
29eda6f593SDavid van Moolenbroek
30eda6f593SDavid van Moolenbroek #include "tmux.h"
31eda6f593SDavid van Moolenbroek
32eda6f593SDavid van Moolenbroek char *status_redraw_get_left(
33eda6f593SDavid van Moolenbroek struct client *, time_t, int, struct grid_cell *, size_t *);
34eda6f593SDavid van Moolenbroek char *status_redraw_get_right(
35eda6f593SDavid van Moolenbroek struct client *, time_t, int, struct grid_cell *, size_t *);
36eda6f593SDavid van Moolenbroek char *status_find_job(struct client *, char **);
37eda6f593SDavid van Moolenbroek void status_job_free(void *);
38eda6f593SDavid van Moolenbroek void status_job_callback(struct job *);
39eda6f593SDavid van Moolenbroek char *status_print(
40eda6f593SDavid van Moolenbroek struct client *, struct winlink *, time_t, struct grid_cell *);
41*0a6a1f1dSLionel Sambuc void status_replace1(struct client *, char **, char **, char *, size_t, int);
42eda6f593SDavid van Moolenbroek void status_message_callback(int, short, void *);
43eda6f593SDavid van Moolenbroek
44eda6f593SDavid van Moolenbroek const char *status_prompt_up_history(u_int *);
45eda6f593SDavid van Moolenbroek const char *status_prompt_down_history(u_int *);
46eda6f593SDavid van Moolenbroek void status_prompt_add_history(const char *);
47eda6f593SDavid van Moolenbroek char *status_prompt_complete(const char *);
48eda6f593SDavid van Moolenbroek
49eda6f593SDavid van Moolenbroek /* Status prompt history. */
50eda6f593SDavid van Moolenbroek ARRAY_DECL(, char *) status_prompt_history = ARRAY_INITIALIZER;
51eda6f593SDavid van Moolenbroek
52eda6f593SDavid van Moolenbroek /* Status output tree. */
53eda6f593SDavid van Moolenbroek RB_GENERATE(status_out_tree, status_out, entry, status_out_cmp);
54eda6f593SDavid van Moolenbroek
55eda6f593SDavid van Moolenbroek /* Output tree comparison function. */
56eda6f593SDavid van Moolenbroek int
status_out_cmp(struct status_out * so1,struct status_out * so2)57eda6f593SDavid van Moolenbroek status_out_cmp(struct status_out *so1, struct status_out *so2)
58eda6f593SDavid van Moolenbroek {
59eda6f593SDavid van Moolenbroek return (strcmp(so1->cmd, so2->cmd));
60eda6f593SDavid van Moolenbroek }
61eda6f593SDavid van Moolenbroek
62*0a6a1f1dSLionel Sambuc /* Get screen line of status line. -1 means off. */
63*0a6a1f1dSLionel Sambuc int
status_at_line(struct client * c)64*0a6a1f1dSLionel Sambuc status_at_line(struct client *c)
65*0a6a1f1dSLionel Sambuc {
66*0a6a1f1dSLionel Sambuc struct session *s = c->session;
67*0a6a1f1dSLionel Sambuc
68*0a6a1f1dSLionel Sambuc if (!options_get_number(&s->options, "status"))
69*0a6a1f1dSLionel Sambuc return (-1);
70*0a6a1f1dSLionel Sambuc
71*0a6a1f1dSLionel Sambuc if (options_get_number(&s->options, "status-position") == 0)
72*0a6a1f1dSLionel Sambuc return (0);
73*0a6a1f1dSLionel Sambuc return (c->tty.sy - 1);
74*0a6a1f1dSLionel Sambuc }
75*0a6a1f1dSLionel Sambuc
76eda6f593SDavid van Moolenbroek /* Retrieve options for left string. */
77eda6f593SDavid van Moolenbroek char *
status_redraw_get_left(struct client * c,time_t t,int utf8flag,struct grid_cell * gc,size_t * size)78eda6f593SDavid van Moolenbroek status_redraw_get_left(struct client *c,
79eda6f593SDavid van Moolenbroek time_t t, int utf8flag, struct grid_cell *gc, size_t *size)
80eda6f593SDavid van Moolenbroek {
81eda6f593SDavid van Moolenbroek struct session *s = c->session;
82eda6f593SDavid van Moolenbroek char *left;
83eda6f593SDavid van Moolenbroek size_t leftlen;
84eda6f593SDavid van Moolenbroek
85*0a6a1f1dSLionel Sambuc style_apply_update(gc, &s->options, "status-left-style");
86eda6f593SDavid van Moolenbroek
87eda6f593SDavid van Moolenbroek left = status_replace(c, NULL,
88eda6f593SDavid van Moolenbroek NULL, NULL, options_get_string(&s->options, "status-left"), t, 1);
89eda6f593SDavid van Moolenbroek
90eda6f593SDavid van Moolenbroek *size = options_get_number(&s->options, "status-left-length");
91eda6f593SDavid van Moolenbroek leftlen = screen_write_cstrlen(utf8flag, "%s", left);
92eda6f593SDavid van Moolenbroek if (leftlen < *size)
93eda6f593SDavid van Moolenbroek *size = leftlen;
94eda6f593SDavid van Moolenbroek return (left);
95eda6f593SDavid van Moolenbroek }
96eda6f593SDavid van Moolenbroek
97eda6f593SDavid van Moolenbroek /* Retrieve options for right string. */
98eda6f593SDavid van Moolenbroek char *
status_redraw_get_right(struct client * c,time_t t,int utf8flag,struct grid_cell * gc,size_t * size)99eda6f593SDavid van Moolenbroek status_redraw_get_right(struct client *c,
100eda6f593SDavid van Moolenbroek time_t t, int utf8flag, struct grid_cell *gc, size_t *size)
101eda6f593SDavid van Moolenbroek {
102eda6f593SDavid van Moolenbroek struct session *s = c->session;
103eda6f593SDavid van Moolenbroek char *right;
104eda6f593SDavid van Moolenbroek size_t rightlen;
105eda6f593SDavid van Moolenbroek
106*0a6a1f1dSLionel Sambuc style_apply_update(gc, &s->options, "status-right-style");
107eda6f593SDavid van Moolenbroek
108eda6f593SDavid van Moolenbroek right = status_replace(c, NULL,
109eda6f593SDavid van Moolenbroek NULL, NULL, options_get_string(&s->options, "status-right"), t, 1);
110eda6f593SDavid van Moolenbroek
111eda6f593SDavid van Moolenbroek *size = options_get_number(&s->options, "status-right-length");
112eda6f593SDavid van Moolenbroek rightlen = screen_write_cstrlen(utf8flag, "%s", right);
113eda6f593SDavid van Moolenbroek if (rightlen < *size)
114eda6f593SDavid van Moolenbroek *size = rightlen;
115eda6f593SDavid van Moolenbroek return (right);
116eda6f593SDavid van Moolenbroek }
117eda6f593SDavid van Moolenbroek
118eda6f593SDavid van Moolenbroek /* Set window at window list position. */
119eda6f593SDavid van Moolenbroek void
status_set_window_at(struct client * c,u_int x)120eda6f593SDavid van Moolenbroek status_set_window_at(struct client *c, u_int x)
121eda6f593SDavid van Moolenbroek {
122eda6f593SDavid van Moolenbroek struct session *s = c->session;
123eda6f593SDavid van Moolenbroek struct winlink *wl;
124eda6f593SDavid van Moolenbroek
125*0a6a1f1dSLionel Sambuc x += c->wlmouse;
126eda6f593SDavid van Moolenbroek RB_FOREACH(wl, winlinks, &s->windows) {
127*0a6a1f1dSLionel Sambuc if (x < wl->status_width && session_select(s, wl->idx) == 0)
128eda6f593SDavid van Moolenbroek server_redraw_session(s);
129eda6f593SDavid van Moolenbroek x -= wl->status_width + 1;
130eda6f593SDavid van Moolenbroek }
131eda6f593SDavid van Moolenbroek }
132eda6f593SDavid van Moolenbroek
133eda6f593SDavid van Moolenbroek /* Draw status for client on the last lines of given context. */
134eda6f593SDavid van Moolenbroek int
status_redraw(struct client * c)135eda6f593SDavid van Moolenbroek status_redraw(struct client *c)
136eda6f593SDavid van Moolenbroek {
137eda6f593SDavid van Moolenbroek struct screen_write_ctx ctx;
138eda6f593SDavid van Moolenbroek struct session *s = c->session;
139eda6f593SDavid van Moolenbroek struct winlink *wl;
140eda6f593SDavid van Moolenbroek struct screen old_status, window_list;
141eda6f593SDavid van Moolenbroek struct grid_cell stdgc, lgc, rgc, gc;
142*0a6a1f1dSLionel Sambuc struct options *oo;
143eda6f593SDavid van Moolenbroek time_t t;
144*0a6a1f1dSLionel Sambuc char *left, *right, *sep;
145eda6f593SDavid van Moolenbroek u_int offset, needed;
146eda6f593SDavid van Moolenbroek u_int wlstart, wlwidth, wlavailable, wloffset, wlsize;
147*0a6a1f1dSLionel Sambuc size_t llen, rlen, seplen;
148eda6f593SDavid van Moolenbroek int larrow, rarrow, utf8flag;
149eda6f593SDavid van Moolenbroek
150eda6f593SDavid van Moolenbroek /* No status line? */
151eda6f593SDavid van Moolenbroek if (c->tty.sy == 0 || !options_get_number(&s->options, "status"))
152eda6f593SDavid van Moolenbroek return (1);
153eda6f593SDavid van Moolenbroek left = right = NULL;
154eda6f593SDavid van Moolenbroek larrow = rarrow = 0;
155eda6f593SDavid van Moolenbroek
156eda6f593SDavid van Moolenbroek /* Update status timer. */
157eda6f593SDavid van Moolenbroek if (gettimeofday(&c->status_timer, NULL) != 0)
158eda6f593SDavid van Moolenbroek fatal("gettimeofday failed");
159eda6f593SDavid van Moolenbroek t = c->status_timer.tv_sec;
160eda6f593SDavid van Moolenbroek
161eda6f593SDavid van Moolenbroek /* Set up default colour. */
162*0a6a1f1dSLionel Sambuc style_apply(&stdgc, &s->options, "status-style");
163eda6f593SDavid van Moolenbroek
164eda6f593SDavid van Moolenbroek /* Create the target screen. */
165eda6f593SDavid van Moolenbroek memcpy(&old_status, &c->status, sizeof old_status);
166eda6f593SDavid van Moolenbroek screen_init(&c->status, c->tty.sx, 1, 0);
167eda6f593SDavid van Moolenbroek screen_write_start(&ctx, NULL, &c->status);
168eda6f593SDavid van Moolenbroek for (offset = 0; offset < c->tty.sx; offset++)
169eda6f593SDavid van Moolenbroek screen_write_putc(&ctx, &stdgc, ' ');
170eda6f593SDavid van Moolenbroek screen_write_stop(&ctx);
171eda6f593SDavid van Moolenbroek
172eda6f593SDavid van Moolenbroek /* If the height is one line, blank status line. */
173eda6f593SDavid van Moolenbroek if (c->tty.sy <= 1)
174eda6f593SDavid van Moolenbroek goto out;
175eda6f593SDavid van Moolenbroek
176eda6f593SDavid van Moolenbroek /* Get UTF-8 flag. */
177eda6f593SDavid van Moolenbroek utf8flag = options_get_number(&s->options, "status-utf8");
178eda6f593SDavid van Moolenbroek
179eda6f593SDavid van Moolenbroek /* Work out left and right strings. */
180eda6f593SDavid van Moolenbroek memcpy(&lgc, &stdgc, sizeof lgc);
181eda6f593SDavid van Moolenbroek left = status_redraw_get_left(c, t, utf8flag, &lgc, &llen);
182eda6f593SDavid van Moolenbroek memcpy(&rgc, &stdgc, sizeof rgc);
183eda6f593SDavid van Moolenbroek right = status_redraw_get_right(c, t, utf8flag, &rgc, &rlen);
184eda6f593SDavid van Moolenbroek
185eda6f593SDavid van Moolenbroek /*
186eda6f593SDavid van Moolenbroek * Figure out how much space we have for the window list. If there
187eda6f593SDavid van Moolenbroek * isn't enough space, just show a blank status line.
188eda6f593SDavid van Moolenbroek */
189eda6f593SDavid van Moolenbroek needed = 0;
190eda6f593SDavid van Moolenbroek if (llen != 0)
191eda6f593SDavid van Moolenbroek needed += llen + 1;
192eda6f593SDavid van Moolenbroek if (rlen != 0)
193eda6f593SDavid van Moolenbroek needed += rlen + 1;
194eda6f593SDavid van Moolenbroek if (c->tty.sx == 0 || c->tty.sx <= needed)
195eda6f593SDavid van Moolenbroek goto out;
196eda6f593SDavid van Moolenbroek wlavailable = c->tty.sx - needed;
197eda6f593SDavid van Moolenbroek
198eda6f593SDavid van Moolenbroek /* Calculate the total size needed for the window list. */
199eda6f593SDavid van Moolenbroek wlstart = wloffset = wlwidth = 0;
200eda6f593SDavid van Moolenbroek RB_FOREACH(wl, winlinks, &s->windows) {
201*0a6a1f1dSLionel Sambuc free(wl->status_text);
202eda6f593SDavid van Moolenbroek memcpy(&wl->status_cell, &stdgc, sizeof wl->status_cell);
203eda6f593SDavid van Moolenbroek wl->status_text = status_print(c, wl, t, &wl->status_cell);
204eda6f593SDavid van Moolenbroek wl->status_width =
205eda6f593SDavid van Moolenbroek screen_write_cstrlen(utf8flag, "%s", wl->status_text);
206eda6f593SDavid van Moolenbroek
207eda6f593SDavid van Moolenbroek if (wl == s->curw)
208eda6f593SDavid van Moolenbroek wloffset = wlwidth;
209*0a6a1f1dSLionel Sambuc
210*0a6a1f1dSLionel Sambuc oo = &wl->window->options;
211*0a6a1f1dSLionel Sambuc sep = options_get_string(oo, "window-status-separator");
212*0a6a1f1dSLionel Sambuc seplen = screen_write_strlen(utf8flag, "%s", sep);
213*0a6a1f1dSLionel Sambuc wlwidth += wl->status_width + seplen;
214eda6f593SDavid van Moolenbroek }
215eda6f593SDavid van Moolenbroek
216eda6f593SDavid van Moolenbroek /* Create a new screen for the window list. */
217eda6f593SDavid van Moolenbroek screen_init(&window_list, wlwidth, 1, 0);
218eda6f593SDavid van Moolenbroek
219eda6f593SDavid van Moolenbroek /* And draw the window list into it. */
220eda6f593SDavid van Moolenbroek screen_write_start(&ctx, NULL, &window_list);
221eda6f593SDavid van Moolenbroek RB_FOREACH(wl, winlinks, &s->windows) {
222eda6f593SDavid van Moolenbroek screen_write_cnputs(&ctx,
223eda6f593SDavid van Moolenbroek -1, &wl->status_cell, utf8flag, "%s", wl->status_text);
224*0a6a1f1dSLionel Sambuc
225*0a6a1f1dSLionel Sambuc oo = &wl->window->options;
226*0a6a1f1dSLionel Sambuc sep = options_get_string(oo, "window-status-separator");
227*0a6a1f1dSLionel Sambuc screen_write_nputs(&ctx, -1, &stdgc, utf8flag, "%s", sep);
228eda6f593SDavid van Moolenbroek }
229eda6f593SDavid van Moolenbroek screen_write_stop(&ctx);
230eda6f593SDavid van Moolenbroek
231eda6f593SDavid van Moolenbroek /* If there is enough space for the total width, skip to draw now. */
232eda6f593SDavid van Moolenbroek if (wlwidth <= wlavailable)
233eda6f593SDavid van Moolenbroek goto draw;
234eda6f593SDavid van Moolenbroek
235eda6f593SDavid van Moolenbroek /* Find size of current window text. */
236eda6f593SDavid van Moolenbroek wlsize = s->curw->status_width;
237eda6f593SDavid van Moolenbroek
238eda6f593SDavid van Moolenbroek /*
239eda6f593SDavid van Moolenbroek * If the current window is already on screen, good to draw from the
240eda6f593SDavid van Moolenbroek * start and just leave off the end.
241eda6f593SDavid van Moolenbroek */
242eda6f593SDavid van Moolenbroek if (wloffset + wlsize < wlavailable) {
243eda6f593SDavid van Moolenbroek if (wlavailable > 0) {
244eda6f593SDavid van Moolenbroek rarrow = 1;
245eda6f593SDavid van Moolenbroek wlavailable--;
246eda6f593SDavid van Moolenbroek }
247eda6f593SDavid van Moolenbroek wlwidth = wlavailable;
248eda6f593SDavid van Moolenbroek } else {
249eda6f593SDavid van Moolenbroek /*
250eda6f593SDavid van Moolenbroek * Work out how many characters we need to omit from the
251eda6f593SDavid van Moolenbroek * start. There are wlavailable characters to fill, and
252eda6f593SDavid van Moolenbroek * wloffset + wlsize must be the last. So, the start character
253eda6f593SDavid van Moolenbroek * is wloffset + wlsize - wlavailable.
254eda6f593SDavid van Moolenbroek */
255eda6f593SDavid van Moolenbroek if (wlavailable > 0) {
256eda6f593SDavid van Moolenbroek larrow = 1;
257eda6f593SDavid van Moolenbroek wlavailable--;
258eda6f593SDavid van Moolenbroek }
259eda6f593SDavid van Moolenbroek
260eda6f593SDavid van Moolenbroek wlstart = wloffset + wlsize - wlavailable;
261eda6f593SDavid van Moolenbroek if (wlavailable > 0 && wlwidth > wlstart + wlavailable + 1) {
262eda6f593SDavid van Moolenbroek rarrow = 1;
263eda6f593SDavid van Moolenbroek wlstart++;
264eda6f593SDavid van Moolenbroek wlavailable--;
265eda6f593SDavid van Moolenbroek }
266eda6f593SDavid van Moolenbroek wlwidth = wlavailable;
267eda6f593SDavid van Moolenbroek }
268eda6f593SDavid van Moolenbroek
269eda6f593SDavid van Moolenbroek /* Bail if anything is now too small too. */
270eda6f593SDavid van Moolenbroek if (wlwidth == 0 || wlavailable == 0) {
271eda6f593SDavid van Moolenbroek screen_free(&window_list);
272eda6f593SDavid van Moolenbroek goto out;
273eda6f593SDavid van Moolenbroek }
274eda6f593SDavid van Moolenbroek
275eda6f593SDavid van Moolenbroek /*
276eda6f593SDavid van Moolenbroek * Now the start position is known, work out the state of the left and
277eda6f593SDavid van Moolenbroek * right arrows.
278eda6f593SDavid van Moolenbroek */
279eda6f593SDavid van Moolenbroek offset = 0;
280eda6f593SDavid van Moolenbroek RB_FOREACH(wl, winlinks, &s->windows) {
281eda6f593SDavid van Moolenbroek if (wl->flags & WINLINK_ALERTFLAGS &&
282eda6f593SDavid van Moolenbroek larrow == 1 && offset < wlstart)
283eda6f593SDavid van Moolenbroek larrow = -1;
284eda6f593SDavid van Moolenbroek
285eda6f593SDavid van Moolenbroek offset += wl->status_width;
286eda6f593SDavid van Moolenbroek
287eda6f593SDavid van Moolenbroek if (wl->flags & WINLINK_ALERTFLAGS &&
288eda6f593SDavid van Moolenbroek rarrow == 1 && offset > wlstart + wlwidth)
289eda6f593SDavid van Moolenbroek rarrow = -1;
290eda6f593SDavid van Moolenbroek }
291eda6f593SDavid van Moolenbroek
292eda6f593SDavid van Moolenbroek draw:
293eda6f593SDavid van Moolenbroek /* Begin drawing. */
294eda6f593SDavid van Moolenbroek screen_write_start(&ctx, NULL, &c->status);
295eda6f593SDavid van Moolenbroek
296eda6f593SDavid van Moolenbroek /* Draw the left string and arrow. */
297eda6f593SDavid van Moolenbroek screen_write_cursormove(&ctx, 0, 0);
298eda6f593SDavid van Moolenbroek if (llen != 0) {
299eda6f593SDavid van Moolenbroek screen_write_cnputs(&ctx, llen, &lgc, utf8flag, "%s", left);
300eda6f593SDavid van Moolenbroek screen_write_putc(&ctx, &stdgc, ' ');
301eda6f593SDavid van Moolenbroek }
302eda6f593SDavid van Moolenbroek if (larrow != 0) {
303eda6f593SDavid van Moolenbroek memcpy(&gc, &stdgc, sizeof gc);
304eda6f593SDavid van Moolenbroek if (larrow == -1)
305eda6f593SDavid van Moolenbroek gc.attr ^= GRID_ATTR_REVERSE;
306eda6f593SDavid van Moolenbroek screen_write_putc(&ctx, &gc, '<');
307eda6f593SDavid van Moolenbroek }
308eda6f593SDavid van Moolenbroek
309eda6f593SDavid van Moolenbroek /* Draw the right string and arrow. */
310eda6f593SDavid van Moolenbroek if (rarrow != 0) {
311eda6f593SDavid van Moolenbroek screen_write_cursormove(&ctx, c->tty.sx - rlen - 2, 0);
312eda6f593SDavid van Moolenbroek memcpy(&gc, &stdgc, sizeof gc);
313eda6f593SDavid van Moolenbroek if (rarrow == -1)
314eda6f593SDavid van Moolenbroek gc.attr ^= GRID_ATTR_REVERSE;
315eda6f593SDavid van Moolenbroek screen_write_putc(&ctx, &gc, '>');
316eda6f593SDavid van Moolenbroek } else
317eda6f593SDavid van Moolenbroek screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, 0);
318eda6f593SDavid van Moolenbroek if (rlen != 0) {
319eda6f593SDavid van Moolenbroek screen_write_putc(&ctx, &stdgc, ' ');
320eda6f593SDavid van Moolenbroek screen_write_cnputs(&ctx, rlen, &rgc, utf8flag, "%s", right);
321eda6f593SDavid van Moolenbroek }
322eda6f593SDavid van Moolenbroek
323eda6f593SDavid van Moolenbroek /* Figure out the offset for the window list. */
324eda6f593SDavid van Moolenbroek if (llen != 0)
325eda6f593SDavid van Moolenbroek wloffset = llen + 1;
326eda6f593SDavid van Moolenbroek else
327eda6f593SDavid van Moolenbroek wloffset = 0;
328eda6f593SDavid van Moolenbroek if (wlwidth < wlavailable) {
329eda6f593SDavid van Moolenbroek switch (options_get_number(&s->options, "status-justify")) {
330eda6f593SDavid van Moolenbroek case 1: /* centered */
331eda6f593SDavid van Moolenbroek wloffset += (wlavailable - wlwidth) / 2;
332eda6f593SDavid van Moolenbroek break;
333eda6f593SDavid van Moolenbroek case 2: /* right */
334eda6f593SDavid van Moolenbroek wloffset += (wlavailable - wlwidth);
335eda6f593SDavid van Moolenbroek break;
336eda6f593SDavid van Moolenbroek }
337eda6f593SDavid van Moolenbroek }
338eda6f593SDavid van Moolenbroek if (larrow != 0)
339eda6f593SDavid van Moolenbroek wloffset++;
340eda6f593SDavid van Moolenbroek
341eda6f593SDavid van Moolenbroek /* Copy the window list. */
342*0a6a1f1dSLionel Sambuc c->wlmouse = -wloffset + wlstart;
343eda6f593SDavid van Moolenbroek screen_write_cursormove(&ctx, wloffset, 0);
344eda6f593SDavid van Moolenbroek screen_write_copy(&ctx, &window_list, wlstart, 0, wlwidth, 1);
345eda6f593SDavid van Moolenbroek screen_free(&window_list);
346eda6f593SDavid van Moolenbroek
347eda6f593SDavid van Moolenbroek screen_write_stop(&ctx);
348eda6f593SDavid van Moolenbroek
349eda6f593SDavid van Moolenbroek out:
350*0a6a1f1dSLionel Sambuc free(left);
351*0a6a1f1dSLionel Sambuc free(right);
352eda6f593SDavid van Moolenbroek
353eda6f593SDavid van Moolenbroek if (grid_compare(c->status.grid, old_status.grid) == 0) {
354eda6f593SDavid van Moolenbroek screen_free(&old_status);
355eda6f593SDavid van Moolenbroek return (0);
356eda6f593SDavid van Moolenbroek }
357eda6f593SDavid van Moolenbroek screen_free(&old_status);
358eda6f593SDavid van Moolenbroek return (1);
359eda6f593SDavid van Moolenbroek }
360eda6f593SDavid van Moolenbroek
361eda6f593SDavid van Moolenbroek /* Replace a single special sequence (prefixed by #). */
362eda6f593SDavid van Moolenbroek void
status_replace1(struct client * c,char ** iptr,char ** optr,char * out,size_t outsize,int jobsflag)363*0a6a1f1dSLionel Sambuc status_replace1(struct client *c, char **iptr, char **optr, char *out,
364eda6f593SDavid van Moolenbroek size_t outsize, int jobsflag)
365eda6f593SDavid van Moolenbroek {
366*0a6a1f1dSLionel Sambuc char ch, tmp[256], *ptr, *endptr;
367eda6f593SDavid van Moolenbroek size_t ptrlen;
368eda6f593SDavid van Moolenbroek long limit;
369eda6f593SDavid van Moolenbroek
370eda6f593SDavid van Moolenbroek errno = 0;
371eda6f593SDavid van Moolenbroek limit = strtol(*iptr, &endptr, 10);
372eda6f593SDavid van Moolenbroek if ((limit == 0 && errno != EINVAL) ||
373eda6f593SDavid van Moolenbroek (limit == LONG_MIN && errno != ERANGE) ||
374eda6f593SDavid van Moolenbroek (limit == LONG_MAX && errno != ERANGE) ||
375eda6f593SDavid van Moolenbroek limit != 0)
376eda6f593SDavid van Moolenbroek *iptr = endptr;
377eda6f593SDavid van Moolenbroek if (limit <= 0)
378eda6f593SDavid van Moolenbroek limit = LONG_MAX;
379eda6f593SDavid van Moolenbroek
380eda6f593SDavid van Moolenbroek switch (*(*iptr)++) {
381eda6f593SDavid van Moolenbroek case '(':
382eda6f593SDavid van Moolenbroek if (!jobsflag) {
383eda6f593SDavid van Moolenbroek ch = ')';
384eda6f593SDavid van Moolenbroek goto skip_to;
385eda6f593SDavid van Moolenbroek }
386eda6f593SDavid van Moolenbroek if ((ptr = status_find_job(c, iptr)) == NULL)
387eda6f593SDavid van Moolenbroek return;
388eda6f593SDavid van Moolenbroek goto do_replace;
389eda6f593SDavid van Moolenbroek case '[':
390eda6f593SDavid van Moolenbroek /*
391eda6f593SDavid van Moolenbroek * Embedded style, handled at display time. Leave present and
392eda6f593SDavid van Moolenbroek * skip input until ].
393eda6f593SDavid van Moolenbroek */
394eda6f593SDavid van Moolenbroek ch = ']';
395eda6f593SDavid van Moolenbroek goto skip_to;
396*0a6a1f1dSLionel Sambuc case '{':
397*0a6a1f1dSLionel Sambuc ptr = __UNCONST("#{");
398*0a6a1f1dSLionel Sambuc goto do_replace;
399eda6f593SDavid van Moolenbroek case '#':
400eda6f593SDavid van Moolenbroek *(*optr)++ = '#';
401eda6f593SDavid van Moolenbroek break;
402*0a6a1f1dSLionel Sambuc default:
403*0a6a1f1dSLionel Sambuc xsnprintf(tmp, sizeof tmp, "#%c", *(*iptr - 1));
404*0a6a1f1dSLionel Sambuc ptr = tmp;
405*0a6a1f1dSLionel Sambuc goto do_replace;
406eda6f593SDavid van Moolenbroek }
407eda6f593SDavid van Moolenbroek
408eda6f593SDavid van Moolenbroek return;
409eda6f593SDavid van Moolenbroek
410eda6f593SDavid van Moolenbroek do_replace:
411eda6f593SDavid van Moolenbroek ptrlen = strlen(ptr);
412eda6f593SDavid van Moolenbroek if ((size_t) limit < ptrlen)
413eda6f593SDavid van Moolenbroek ptrlen = limit;
414eda6f593SDavid van Moolenbroek
415eda6f593SDavid van Moolenbroek if (*optr + ptrlen >= out + outsize - 1)
416eda6f593SDavid van Moolenbroek return;
417eda6f593SDavid van Moolenbroek while (ptrlen > 0 && *ptr != '\0') {
418eda6f593SDavid van Moolenbroek *(*optr)++ = *ptr++;
419eda6f593SDavid van Moolenbroek ptrlen--;
420eda6f593SDavid van Moolenbroek }
421eda6f593SDavid van Moolenbroek
422eda6f593SDavid van Moolenbroek return;
423eda6f593SDavid van Moolenbroek
424eda6f593SDavid van Moolenbroek skip_to:
425eda6f593SDavid van Moolenbroek *(*optr)++ = '#';
426eda6f593SDavid van Moolenbroek
427eda6f593SDavid van Moolenbroek (*iptr)--; /* include ch */
428eda6f593SDavid van Moolenbroek while (**iptr != ch && **iptr != '\0') {
429eda6f593SDavid van Moolenbroek if (*optr >= out + outsize - 1)
430eda6f593SDavid van Moolenbroek break;
431eda6f593SDavid van Moolenbroek *(*optr)++ = *(*iptr)++;
432eda6f593SDavid van Moolenbroek }
433eda6f593SDavid van Moolenbroek }
434eda6f593SDavid van Moolenbroek
435eda6f593SDavid van Moolenbroek /* Replace special sequences in fmt. */
436eda6f593SDavid van Moolenbroek char *
status_replace(struct client * c,struct session * s,struct winlink * wl,struct window_pane * wp,const char * fmt,time_t t,int jobsflag)437eda6f593SDavid van Moolenbroek status_replace(struct client *c, struct session *s, struct winlink *wl,
438eda6f593SDavid van Moolenbroek struct window_pane *wp, const char *fmt, time_t t, int jobsflag)
439eda6f593SDavid van Moolenbroek {
440eda6f593SDavid van Moolenbroek static char out[BUFSIZ];
441*0a6a1f1dSLionel Sambuc char in[BUFSIZ], ch, *iptr, *optr, *expanded;
442*0a6a1f1dSLionel Sambuc size_t len;
443*0a6a1f1dSLionel Sambuc struct format_tree *ft;
444eda6f593SDavid van Moolenbroek
445*0a6a1f1dSLionel Sambuc if (fmt == NULL)
446*0a6a1f1dSLionel Sambuc return (xstrdup(""));
447*0a6a1f1dSLionel Sambuc
448*0a6a1f1dSLionel Sambuc if (s == NULL && c != NULL)
449*0a6a1f1dSLionel Sambuc s = c->session;
450*0a6a1f1dSLionel Sambuc if (wl == NULL && s != NULL)
451*0a6a1f1dSLionel Sambuc wl = s->curw;
452*0a6a1f1dSLionel Sambuc if (wp == NULL && wl != NULL)
453*0a6a1f1dSLionel Sambuc wp = wl->window->active;
454*0a6a1f1dSLionel Sambuc
455*0a6a1f1dSLionel Sambuc len = strftime(in, sizeof in, fmt, localtime(&t));
456*0a6a1f1dSLionel Sambuc in[len] = '\0';
457eda6f593SDavid van Moolenbroek
458eda6f593SDavid van Moolenbroek iptr = in;
459eda6f593SDavid van Moolenbroek optr = out;
460eda6f593SDavid van Moolenbroek
461eda6f593SDavid van Moolenbroek while (*iptr != '\0') {
462eda6f593SDavid van Moolenbroek if (optr >= out + (sizeof out) - 1)
463eda6f593SDavid van Moolenbroek break;
464eda6f593SDavid van Moolenbroek ch = *iptr++;
465eda6f593SDavid van Moolenbroek
466eda6f593SDavid van Moolenbroek if (ch != '#' || *iptr == '\0') {
467eda6f593SDavid van Moolenbroek *optr++ = ch;
468eda6f593SDavid van Moolenbroek continue;
469eda6f593SDavid van Moolenbroek }
470*0a6a1f1dSLionel Sambuc status_replace1(c, &iptr, &optr, out, sizeof out, jobsflag);
471eda6f593SDavid van Moolenbroek }
472eda6f593SDavid van Moolenbroek *optr = '\0';
473eda6f593SDavid van Moolenbroek
474*0a6a1f1dSLionel Sambuc ft = format_create();
475*0a6a1f1dSLionel Sambuc if (c != NULL)
476*0a6a1f1dSLionel Sambuc format_client(ft, c);
477*0a6a1f1dSLionel Sambuc if (s != NULL)
478*0a6a1f1dSLionel Sambuc format_session(ft, s);
479*0a6a1f1dSLionel Sambuc if (s != NULL && wl != NULL)
480*0a6a1f1dSLionel Sambuc format_winlink(ft, s, wl);
481*0a6a1f1dSLionel Sambuc if (wp != NULL)
482*0a6a1f1dSLionel Sambuc format_window_pane(ft, wp);
483*0a6a1f1dSLionel Sambuc expanded = format_expand(ft, out);
484*0a6a1f1dSLionel Sambuc format_free(ft);
485*0a6a1f1dSLionel Sambuc return (expanded);
486eda6f593SDavid van Moolenbroek }
487eda6f593SDavid van Moolenbroek
488eda6f593SDavid van Moolenbroek /* Figure out job name and get its result, starting it off if necessary. */
489eda6f593SDavid van Moolenbroek char *
status_find_job(struct client * c,char ** iptr)490eda6f593SDavid van Moolenbroek status_find_job(struct client *c, char **iptr)
491eda6f593SDavid van Moolenbroek {
492eda6f593SDavid van Moolenbroek struct status_out *so, so_find;
493eda6f593SDavid van Moolenbroek char *cmd;
494eda6f593SDavid van Moolenbroek int lastesc;
495eda6f593SDavid van Moolenbroek size_t len;
496eda6f593SDavid van Moolenbroek
497eda6f593SDavid van Moolenbroek if (**iptr == '\0')
498eda6f593SDavid van Moolenbroek return (NULL);
499eda6f593SDavid van Moolenbroek if (**iptr == ')') { /* no command given */
500eda6f593SDavid van Moolenbroek (*iptr)++;
501eda6f593SDavid van Moolenbroek return (NULL);
502eda6f593SDavid van Moolenbroek }
503eda6f593SDavid van Moolenbroek
504eda6f593SDavid van Moolenbroek cmd = xmalloc(strlen(*iptr) + 1);
505eda6f593SDavid van Moolenbroek len = 0;
506eda6f593SDavid van Moolenbroek
507eda6f593SDavid van Moolenbroek lastesc = 0;
508eda6f593SDavid van Moolenbroek for (; **iptr != '\0'; (*iptr)++) {
509eda6f593SDavid van Moolenbroek if (!lastesc && **iptr == ')')
510eda6f593SDavid van Moolenbroek break; /* unescaped ) is the end */
511eda6f593SDavid van Moolenbroek if (!lastesc && **iptr == '\\') {
512eda6f593SDavid van Moolenbroek lastesc = 1;
513eda6f593SDavid van Moolenbroek continue; /* skip \ if not escaped */
514eda6f593SDavid van Moolenbroek }
515eda6f593SDavid van Moolenbroek lastesc = 0;
516eda6f593SDavid van Moolenbroek cmd[len++] = **iptr;
517eda6f593SDavid van Moolenbroek }
518eda6f593SDavid van Moolenbroek if (**iptr == '\0') /* no terminating ) */ {
519*0a6a1f1dSLionel Sambuc free(cmd);
520eda6f593SDavid van Moolenbroek return (NULL);
521eda6f593SDavid van Moolenbroek }
522eda6f593SDavid van Moolenbroek (*iptr)++; /* skip final ) */
523eda6f593SDavid van Moolenbroek cmd[len] = '\0';
524eda6f593SDavid van Moolenbroek
525eda6f593SDavid van Moolenbroek /* First try in the new tree. */
526eda6f593SDavid van Moolenbroek so_find.cmd = cmd;
527eda6f593SDavid van Moolenbroek so = RB_FIND(status_out_tree, &c->status_new, &so_find);
528*0a6a1f1dSLionel Sambuc if (so != NULL && so->out != NULL) {
529*0a6a1f1dSLionel Sambuc free(cmd);
530eda6f593SDavid van Moolenbroek return (so->out);
531*0a6a1f1dSLionel Sambuc }
532eda6f593SDavid van Moolenbroek
533eda6f593SDavid van Moolenbroek /* If not found at all, start the job and add to the tree. */
534eda6f593SDavid van Moolenbroek if (so == NULL) {
535*0a6a1f1dSLionel Sambuc job_run(cmd, NULL, status_job_callback, status_job_free, c);
536eda6f593SDavid van Moolenbroek c->references++;
537eda6f593SDavid van Moolenbroek
538eda6f593SDavid van Moolenbroek so = xmalloc(sizeof *so);
539eda6f593SDavid van Moolenbroek so->cmd = xstrdup(cmd);
540eda6f593SDavid van Moolenbroek so->out = NULL;
541eda6f593SDavid van Moolenbroek RB_INSERT(status_out_tree, &c->status_new, so);
542eda6f593SDavid van Moolenbroek }
543eda6f593SDavid van Moolenbroek
544eda6f593SDavid van Moolenbroek /* Lookup in the old tree. */
545eda6f593SDavid van Moolenbroek so_find.cmd = cmd;
546eda6f593SDavid van Moolenbroek so = RB_FIND(status_out_tree, &c->status_old, &so_find);
547*0a6a1f1dSLionel Sambuc free(cmd);
548eda6f593SDavid van Moolenbroek if (so != NULL)
549eda6f593SDavid van Moolenbroek return (so->out);
550eda6f593SDavid van Moolenbroek return (NULL);
551eda6f593SDavid van Moolenbroek }
552eda6f593SDavid van Moolenbroek
553eda6f593SDavid van Moolenbroek /* Free job tree. */
554eda6f593SDavid van Moolenbroek void
status_free_jobs(struct status_out_tree * sotree)555eda6f593SDavid van Moolenbroek status_free_jobs(struct status_out_tree *sotree)
556eda6f593SDavid van Moolenbroek {
557eda6f593SDavid van Moolenbroek struct status_out *so, *so_next;
558eda6f593SDavid van Moolenbroek
559eda6f593SDavid van Moolenbroek so_next = RB_MIN(status_out_tree, sotree);
560eda6f593SDavid van Moolenbroek while (so_next != NULL) {
561eda6f593SDavid van Moolenbroek so = so_next;
562eda6f593SDavid van Moolenbroek so_next = RB_NEXT(status_out_tree, sotree, so);
563eda6f593SDavid van Moolenbroek
564eda6f593SDavid van Moolenbroek RB_REMOVE(status_out_tree, sotree, so);
565*0a6a1f1dSLionel Sambuc free(so->out);
566*0a6a1f1dSLionel Sambuc free(so->cmd);
567*0a6a1f1dSLionel Sambuc free(so);
568eda6f593SDavid van Moolenbroek }
569eda6f593SDavid van Moolenbroek }
570eda6f593SDavid van Moolenbroek
571eda6f593SDavid van Moolenbroek /* Update jobs on status interval. */
572eda6f593SDavid van Moolenbroek void
status_update_jobs(struct client * c)573eda6f593SDavid van Moolenbroek status_update_jobs(struct client *c)
574eda6f593SDavid van Moolenbroek {
575eda6f593SDavid van Moolenbroek /* Free the old tree. */
576eda6f593SDavid van Moolenbroek status_free_jobs(&c->status_old);
577eda6f593SDavid van Moolenbroek
578eda6f593SDavid van Moolenbroek /* Move the new to old. */
579eda6f593SDavid van Moolenbroek memcpy(&c->status_old, &c->status_new, sizeof c->status_old);
580eda6f593SDavid van Moolenbroek RB_INIT(&c->status_new);
581eda6f593SDavid van Moolenbroek }
582eda6f593SDavid van Moolenbroek
583eda6f593SDavid van Moolenbroek /* Free status job. */
584eda6f593SDavid van Moolenbroek void
status_job_free(void * data)585eda6f593SDavid van Moolenbroek status_job_free(void *data)
586eda6f593SDavid van Moolenbroek {
587eda6f593SDavid van Moolenbroek struct client *c = data;
588eda6f593SDavid van Moolenbroek
589eda6f593SDavid van Moolenbroek c->references--;
590eda6f593SDavid van Moolenbroek }
591eda6f593SDavid van Moolenbroek
592eda6f593SDavid van Moolenbroek /* Job has finished: save its result. */
593eda6f593SDavid van Moolenbroek void
status_job_callback(struct job * job)594eda6f593SDavid van Moolenbroek status_job_callback(struct job *job)
595eda6f593SDavid van Moolenbroek {
596eda6f593SDavid van Moolenbroek struct client *c = job->data;
597eda6f593SDavid van Moolenbroek struct status_out *so, so_find;
598eda6f593SDavid van Moolenbroek char *line, *buf;
599eda6f593SDavid van Moolenbroek size_t len;
600eda6f593SDavid van Moolenbroek
601eda6f593SDavid van Moolenbroek if (c->flags & CLIENT_DEAD)
602eda6f593SDavid van Moolenbroek return;
603eda6f593SDavid van Moolenbroek
604eda6f593SDavid van Moolenbroek so_find.cmd = job->cmd;
605eda6f593SDavid van Moolenbroek so = RB_FIND(status_out_tree, &c->status_new, &so_find);
606eda6f593SDavid van Moolenbroek if (so == NULL || so->out != NULL)
607eda6f593SDavid van Moolenbroek return;
608eda6f593SDavid van Moolenbroek
609eda6f593SDavid van Moolenbroek buf = NULL;
610eda6f593SDavid van Moolenbroek if ((line = evbuffer_readline(job->event->input)) == NULL) {
611eda6f593SDavid van Moolenbroek len = EVBUFFER_LENGTH(job->event->input);
612eda6f593SDavid van Moolenbroek buf = xmalloc(len + 1);
613eda6f593SDavid van Moolenbroek if (len != 0)
614eda6f593SDavid van Moolenbroek memcpy(buf, EVBUFFER_DATA(job->event->input), len);
615eda6f593SDavid van Moolenbroek buf[len] = '\0';
616eda6f593SDavid van Moolenbroek } else
617*0a6a1f1dSLionel Sambuc buf = line;
618eda6f593SDavid van Moolenbroek
619eda6f593SDavid van Moolenbroek so->out = buf;
620eda6f593SDavid van Moolenbroek server_status_client(c);
621eda6f593SDavid van Moolenbroek }
622eda6f593SDavid van Moolenbroek
623eda6f593SDavid van Moolenbroek /* Return winlink status line entry and adjust gc as necessary. */
624eda6f593SDavid van Moolenbroek char *
status_print(struct client * c,struct winlink * wl,time_t t,struct grid_cell * gc)625eda6f593SDavid van Moolenbroek status_print(
626eda6f593SDavid van Moolenbroek struct client *c, struct winlink *wl, time_t t, struct grid_cell *gc)
627eda6f593SDavid van Moolenbroek {
628eda6f593SDavid van Moolenbroek struct options *oo = &wl->window->options;
629eda6f593SDavid van Moolenbroek struct session *s = c->session;
630eda6f593SDavid van Moolenbroek const char *fmt;
631eda6f593SDavid van Moolenbroek char *text;
632eda6f593SDavid van Moolenbroek
633*0a6a1f1dSLionel Sambuc style_apply_update(gc, oo, "window-status-style");
634eda6f593SDavid van Moolenbroek fmt = options_get_string(oo, "window-status-format");
635eda6f593SDavid van Moolenbroek if (wl == s->curw) {
636*0a6a1f1dSLionel Sambuc style_apply_update(gc, oo, "window-status-current-style");
637eda6f593SDavid van Moolenbroek fmt = options_get_string(oo, "window-status-current-format");
638eda6f593SDavid van Moolenbroek }
639*0a6a1f1dSLionel Sambuc if (wl == TAILQ_FIRST(&s->lastw))
640*0a6a1f1dSLionel Sambuc style_apply_update(gc, oo, "window-status-last-style");
641eda6f593SDavid van Moolenbroek
642*0a6a1f1dSLionel Sambuc if (wl->flags & WINLINK_BELL)
643*0a6a1f1dSLionel Sambuc style_apply_update(gc, oo, "window-status-bell-style");
644*0a6a1f1dSLionel Sambuc else if (wl->flags & WINLINK_CONTENT)
645*0a6a1f1dSLionel Sambuc style_apply_update(gc, oo, "window-status-content-style");
646*0a6a1f1dSLionel Sambuc else if (wl->flags & (WINLINK_ACTIVITY|WINLINK_SILENCE))
647*0a6a1f1dSLionel Sambuc style_apply_update(gc, oo, "window-status-activity-style");
648eda6f593SDavid van Moolenbroek
649eda6f593SDavid van Moolenbroek text = status_replace(c, NULL, wl, NULL, fmt, t, 1);
650eda6f593SDavid van Moolenbroek return (text);
651eda6f593SDavid van Moolenbroek }
652eda6f593SDavid van Moolenbroek
653eda6f593SDavid van Moolenbroek /* Set a status line message. */
654eda6f593SDavid van Moolenbroek void printflike2
status_message_set(struct client * c,const char * fmt,...)655eda6f593SDavid van Moolenbroek status_message_set(struct client *c, const char *fmt, ...)
656eda6f593SDavid van Moolenbroek {
657eda6f593SDavid van Moolenbroek struct timeval tv;
658eda6f593SDavid van Moolenbroek struct session *s = c->session;
659eda6f593SDavid van Moolenbroek struct message_entry *msg;
660eda6f593SDavid van Moolenbroek va_list ap;
661eda6f593SDavid van Moolenbroek int delay;
662eda6f593SDavid van Moolenbroek u_int i, limit;
663eda6f593SDavid van Moolenbroek
664eda6f593SDavid van Moolenbroek status_prompt_clear(c);
665eda6f593SDavid van Moolenbroek status_message_clear(c);
666eda6f593SDavid van Moolenbroek
667eda6f593SDavid van Moolenbroek va_start(ap, fmt);
668eda6f593SDavid van Moolenbroek xvasprintf(&c->message_string, fmt, ap);
669eda6f593SDavid van Moolenbroek va_end(ap);
670eda6f593SDavid van Moolenbroek
671eda6f593SDavid van Moolenbroek ARRAY_EXPAND(&c->message_log, 1);
672eda6f593SDavid van Moolenbroek msg = &ARRAY_LAST(&c->message_log);
673eda6f593SDavid van Moolenbroek msg->msg_time = time(NULL);
674eda6f593SDavid van Moolenbroek msg->msg = xstrdup(c->message_string);
675eda6f593SDavid van Moolenbroek
676eda6f593SDavid van Moolenbroek if (s == NULL)
677eda6f593SDavid van Moolenbroek limit = 0;
678eda6f593SDavid van Moolenbroek else
679eda6f593SDavid van Moolenbroek limit = options_get_number(&s->options, "message-limit");
680eda6f593SDavid van Moolenbroek if (ARRAY_LENGTH(&c->message_log) > limit) {
681eda6f593SDavid van Moolenbroek limit = ARRAY_LENGTH(&c->message_log) - limit;
682eda6f593SDavid van Moolenbroek for (i = 0; i < limit; i++) {
683eda6f593SDavid van Moolenbroek msg = &ARRAY_FIRST(&c->message_log);
684*0a6a1f1dSLionel Sambuc free(msg->msg);
685eda6f593SDavid van Moolenbroek ARRAY_REMOVE(&c->message_log, 0);
686eda6f593SDavid van Moolenbroek }
687eda6f593SDavid van Moolenbroek }
688eda6f593SDavid van Moolenbroek
689eda6f593SDavid van Moolenbroek delay = options_get_number(&c->session->options, "display-time");
690eda6f593SDavid van Moolenbroek tv.tv_sec = delay / 1000;
691eda6f593SDavid van Moolenbroek tv.tv_usec = (delay % 1000) * 1000L;
692eda6f593SDavid van Moolenbroek
693*0a6a1f1dSLionel Sambuc if (event_initialized(&c->message_timer))
694eda6f593SDavid van Moolenbroek evtimer_del(&c->message_timer);
695eda6f593SDavid van Moolenbroek evtimer_set(&c->message_timer, status_message_callback, c);
696eda6f593SDavid van Moolenbroek evtimer_add(&c->message_timer, &tv);
697eda6f593SDavid van Moolenbroek
698eda6f593SDavid van Moolenbroek c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
699eda6f593SDavid van Moolenbroek c->flags |= CLIENT_STATUS;
700eda6f593SDavid van Moolenbroek }
701eda6f593SDavid van Moolenbroek
702eda6f593SDavid van Moolenbroek /* Clear status line message. */
703eda6f593SDavid van Moolenbroek void
status_message_clear(struct client * c)704eda6f593SDavid van Moolenbroek status_message_clear(struct client *c)
705eda6f593SDavid van Moolenbroek {
706eda6f593SDavid van Moolenbroek if (c->message_string == NULL)
707eda6f593SDavid van Moolenbroek return;
708eda6f593SDavid van Moolenbroek
709*0a6a1f1dSLionel Sambuc free(c->message_string);
710eda6f593SDavid van Moolenbroek c->message_string = NULL;
711eda6f593SDavid van Moolenbroek
712eda6f593SDavid van Moolenbroek c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
713eda6f593SDavid van Moolenbroek c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */
714eda6f593SDavid van Moolenbroek
715eda6f593SDavid van Moolenbroek screen_reinit(&c->status);
716eda6f593SDavid van Moolenbroek }
717eda6f593SDavid van Moolenbroek
718eda6f593SDavid van Moolenbroek /* Clear status line message after timer expires. */
719eda6f593SDavid van Moolenbroek void
status_message_callback(unused int fd,unused short event,void * data)720eda6f593SDavid van Moolenbroek status_message_callback(unused int fd, unused short event, void *data)
721eda6f593SDavid van Moolenbroek {
722eda6f593SDavid van Moolenbroek struct client *c = data;
723eda6f593SDavid van Moolenbroek
724eda6f593SDavid van Moolenbroek status_message_clear(c);
725eda6f593SDavid van Moolenbroek }
726eda6f593SDavid van Moolenbroek
727eda6f593SDavid van Moolenbroek /* Draw client message on status line of present else on last line. */
728eda6f593SDavid van Moolenbroek int
status_message_redraw(struct client * c)729eda6f593SDavid van Moolenbroek status_message_redraw(struct client *c)
730eda6f593SDavid van Moolenbroek {
731eda6f593SDavid van Moolenbroek struct screen_write_ctx ctx;
732eda6f593SDavid van Moolenbroek struct session *s = c->session;
733eda6f593SDavid van Moolenbroek struct screen old_status;
734eda6f593SDavid van Moolenbroek size_t len;
735eda6f593SDavid van Moolenbroek struct grid_cell gc;
736eda6f593SDavid van Moolenbroek int utf8flag;
737eda6f593SDavid van Moolenbroek
738eda6f593SDavid van Moolenbroek if (c->tty.sx == 0 || c->tty.sy == 0)
739eda6f593SDavid van Moolenbroek return (0);
740eda6f593SDavid van Moolenbroek memcpy(&old_status, &c->status, sizeof old_status);
741eda6f593SDavid van Moolenbroek screen_init(&c->status, c->tty.sx, 1, 0);
742eda6f593SDavid van Moolenbroek
743eda6f593SDavid van Moolenbroek utf8flag = options_get_number(&s->options, "status-utf8");
744eda6f593SDavid van Moolenbroek
745eda6f593SDavid van Moolenbroek len = screen_write_strlen(utf8flag, "%s", c->message_string);
746eda6f593SDavid van Moolenbroek if (len > c->tty.sx)
747eda6f593SDavid van Moolenbroek len = c->tty.sx;
748eda6f593SDavid van Moolenbroek
749*0a6a1f1dSLionel Sambuc style_apply(&gc, &s->options, "message-style");
750eda6f593SDavid van Moolenbroek
751eda6f593SDavid van Moolenbroek screen_write_start(&ctx, NULL, &c->status);
752eda6f593SDavid van Moolenbroek
753eda6f593SDavid van Moolenbroek screen_write_cursormove(&ctx, 0, 0);
754eda6f593SDavid van Moolenbroek screen_write_nputs(&ctx, len, &gc, utf8flag, "%s", c->message_string);
755eda6f593SDavid van Moolenbroek for (; len < c->tty.sx; len++)
756eda6f593SDavid van Moolenbroek screen_write_putc(&ctx, &gc, ' ');
757eda6f593SDavid van Moolenbroek
758eda6f593SDavid van Moolenbroek screen_write_stop(&ctx);
759eda6f593SDavid van Moolenbroek
760eda6f593SDavid van Moolenbroek if (grid_compare(c->status.grid, old_status.grid) == 0) {
761eda6f593SDavid van Moolenbroek screen_free(&old_status);
762eda6f593SDavid van Moolenbroek return (0);
763eda6f593SDavid van Moolenbroek }
764eda6f593SDavid van Moolenbroek screen_free(&old_status);
765eda6f593SDavid van Moolenbroek return (1);
766eda6f593SDavid van Moolenbroek }
767eda6f593SDavid van Moolenbroek
768eda6f593SDavid van Moolenbroek /* Enable status line prompt. */
769eda6f593SDavid van Moolenbroek void
status_prompt_set(struct client * c,const char * msg,const char * input,int (* callbackfn)(void *,const char *),void (* freefn)(void *),void * data,int flags)770eda6f593SDavid van Moolenbroek status_prompt_set(struct client *c, const char *msg, const char *input,
771eda6f593SDavid van Moolenbroek int (*callbackfn)(void *, const char *), void (*freefn)(void *),
772eda6f593SDavid van Moolenbroek void *data, int flags)
773eda6f593SDavid van Moolenbroek {
774eda6f593SDavid van Moolenbroek int keys;
775eda6f593SDavid van Moolenbroek
776eda6f593SDavid van Moolenbroek status_message_clear(c);
777eda6f593SDavid van Moolenbroek status_prompt_clear(c);
778eda6f593SDavid van Moolenbroek
779eda6f593SDavid van Moolenbroek c->prompt_string = status_replace(c, NULL, NULL, NULL, msg,
780eda6f593SDavid van Moolenbroek time(NULL), 0);
781eda6f593SDavid van Moolenbroek
782eda6f593SDavid van Moolenbroek c->prompt_buffer = status_replace(c, NULL, NULL, NULL, input,
783eda6f593SDavid van Moolenbroek time(NULL), 0);
784eda6f593SDavid van Moolenbroek c->prompt_index = strlen(c->prompt_buffer);
785eda6f593SDavid van Moolenbroek
786eda6f593SDavid van Moolenbroek c->prompt_callbackfn = callbackfn;
787eda6f593SDavid van Moolenbroek c->prompt_freefn = freefn;
788eda6f593SDavid van Moolenbroek c->prompt_data = data;
789eda6f593SDavid van Moolenbroek
790eda6f593SDavid van Moolenbroek c->prompt_hindex = 0;
791eda6f593SDavid van Moolenbroek
792eda6f593SDavid van Moolenbroek c->prompt_flags = flags;
793eda6f593SDavid van Moolenbroek
794eda6f593SDavid van Moolenbroek keys = options_get_number(&c->session->options, "status-keys");
795eda6f593SDavid van Moolenbroek if (keys == MODEKEY_EMACS)
796eda6f593SDavid van Moolenbroek mode_key_init(&c->prompt_mdata, &mode_key_tree_emacs_edit);
797eda6f593SDavid van Moolenbroek else
798eda6f593SDavid van Moolenbroek mode_key_init(&c->prompt_mdata, &mode_key_tree_vi_edit);
799eda6f593SDavid van Moolenbroek
800eda6f593SDavid van Moolenbroek c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
801eda6f593SDavid van Moolenbroek c->flags |= CLIENT_STATUS;
802eda6f593SDavid van Moolenbroek }
803eda6f593SDavid van Moolenbroek
804eda6f593SDavid van Moolenbroek /* Remove status line prompt. */
805eda6f593SDavid van Moolenbroek void
status_prompt_clear(struct client * c)806eda6f593SDavid van Moolenbroek status_prompt_clear(struct client *c)
807eda6f593SDavid van Moolenbroek {
808eda6f593SDavid van Moolenbroek if (c->prompt_string == NULL)
809eda6f593SDavid van Moolenbroek return;
810eda6f593SDavid van Moolenbroek
811eda6f593SDavid van Moolenbroek if (c->prompt_freefn != NULL && c->prompt_data != NULL)
812eda6f593SDavid van Moolenbroek c->prompt_freefn(c->prompt_data);
813eda6f593SDavid van Moolenbroek
814*0a6a1f1dSLionel Sambuc free(c->prompt_string);
815eda6f593SDavid van Moolenbroek c->prompt_string = NULL;
816eda6f593SDavid van Moolenbroek
817*0a6a1f1dSLionel Sambuc free(c->prompt_buffer);
818eda6f593SDavid van Moolenbroek c->prompt_buffer = NULL;
819eda6f593SDavid van Moolenbroek
820eda6f593SDavid van Moolenbroek c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE);
821eda6f593SDavid van Moolenbroek c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */
822eda6f593SDavid van Moolenbroek
823eda6f593SDavid van Moolenbroek screen_reinit(&c->status);
824eda6f593SDavid van Moolenbroek }
825eda6f593SDavid van Moolenbroek
826eda6f593SDavid van Moolenbroek /* Update status line prompt with a new prompt string. */
827eda6f593SDavid van Moolenbroek void
status_prompt_update(struct client * c,const char * msg,const char * input)828eda6f593SDavid van Moolenbroek status_prompt_update(struct client *c, const char *msg, const char *input)
829eda6f593SDavid van Moolenbroek {
830*0a6a1f1dSLionel Sambuc free(c->prompt_string);
831eda6f593SDavid van Moolenbroek c->prompt_string = status_replace(c, NULL, NULL, NULL, msg,
832eda6f593SDavid van Moolenbroek time(NULL), 0);
833eda6f593SDavid van Moolenbroek
834*0a6a1f1dSLionel Sambuc free(c->prompt_buffer);
835eda6f593SDavid van Moolenbroek c->prompt_buffer = status_replace(c, NULL, NULL, NULL, input,
836eda6f593SDavid van Moolenbroek time(NULL), 0);
837eda6f593SDavid van Moolenbroek c->prompt_index = strlen(c->prompt_buffer);
838eda6f593SDavid van Moolenbroek
839eda6f593SDavid van Moolenbroek c->prompt_hindex = 0;
840eda6f593SDavid van Moolenbroek
841eda6f593SDavid van Moolenbroek c->flags |= CLIENT_STATUS;
842eda6f593SDavid van Moolenbroek }
843eda6f593SDavid van Moolenbroek
844eda6f593SDavid van Moolenbroek /* Draw client prompt on status line of present else on last line. */
845eda6f593SDavid van Moolenbroek int
status_prompt_redraw(struct client * c)846eda6f593SDavid van Moolenbroek status_prompt_redraw(struct client *c)
847eda6f593SDavid van Moolenbroek {
848eda6f593SDavid van Moolenbroek struct screen_write_ctx ctx;
849eda6f593SDavid van Moolenbroek struct session *s = c->session;
850eda6f593SDavid van Moolenbroek struct screen old_status;
851eda6f593SDavid van Moolenbroek size_t i, size, left, len, off;
852eda6f593SDavid van Moolenbroek struct grid_cell gc, *gcp;
853eda6f593SDavid van Moolenbroek int utf8flag;
854eda6f593SDavid van Moolenbroek
855eda6f593SDavid van Moolenbroek if (c->tty.sx == 0 || c->tty.sy == 0)
856eda6f593SDavid van Moolenbroek return (0);
857eda6f593SDavid van Moolenbroek memcpy(&old_status, &c->status, sizeof old_status);
858eda6f593SDavid van Moolenbroek screen_init(&c->status, c->tty.sx, 1, 0);
859eda6f593SDavid van Moolenbroek
860eda6f593SDavid van Moolenbroek utf8flag = options_get_number(&s->options, "status-utf8");
861eda6f593SDavid van Moolenbroek
862eda6f593SDavid van Moolenbroek len = screen_write_strlen(utf8flag, "%s", c->prompt_string);
863eda6f593SDavid van Moolenbroek if (len > c->tty.sx)
864eda6f593SDavid van Moolenbroek len = c->tty.sx;
865eda6f593SDavid van Moolenbroek off = 0;
866eda6f593SDavid van Moolenbroek
867*0a6a1f1dSLionel Sambuc /* Change colours for command mode. */
868*0a6a1f1dSLionel Sambuc if (c->prompt_mdata.mode == 1)
869*0a6a1f1dSLionel Sambuc style_apply(&gc, &s->options, "message-command-style");
870*0a6a1f1dSLionel Sambuc else
871*0a6a1f1dSLionel Sambuc style_apply(&gc, &s->options, "message-style");
872eda6f593SDavid van Moolenbroek
873eda6f593SDavid van Moolenbroek screen_write_start(&ctx, NULL, &c->status);
874eda6f593SDavid van Moolenbroek
875eda6f593SDavid van Moolenbroek screen_write_cursormove(&ctx, 0, 0);
876eda6f593SDavid van Moolenbroek screen_write_nputs(&ctx, len, &gc, utf8flag, "%s", c->prompt_string);
877eda6f593SDavid van Moolenbroek
878eda6f593SDavid van Moolenbroek left = c->tty.sx - len;
879eda6f593SDavid van Moolenbroek if (left != 0) {
880eda6f593SDavid van Moolenbroek size = screen_write_strlen(utf8flag, "%s", c->prompt_buffer);
881eda6f593SDavid van Moolenbroek if (c->prompt_index >= left) {
882eda6f593SDavid van Moolenbroek off = c->prompt_index - left + 1;
883eda6f593SDavid van Moolenbroek if (c->prompt_index == size)
884eda6f593SDavid van Moolenbroek left--;
885eda6f593SDavid van Moolenbroek size = left;
886eda6f593SDavid van Moolenbroek }
887eda6f593SDavid van Moolenbroek screen_write_nputs(
888eda6f593SDavid van Moolenbroek &ctx, left, &gc, utf8flag, "%s", c->prompt_buffer + off);
889eda6f593SDavid van Moolenbroek
890eda6f593SDavid van Moolenbroek for (i = len + size; i < c->tty.sx; i++)
891eda6f593SDavid van Moolenbroek screen_write_putc(&ctx, &gc, ' ');
892eda6f593SDavid van Moolenbroek }
893eda6f593SDavid van Moolenbroek
894eda6f593SDavid van Moolenbroek screen_write_stop(&ctx);
895eda6f593SDavid van Moolenbroek
896eda6f593SDavid van Moolenbroek /* Apply fake cursor. */
897eda6f593SDavid van Moolenbroek off = len + c->prompt_index - off;
898eda6f593SDavid van Moolenbroek gcp = grid_view_get_cell(c->status.grid, off, 0);
899eda6f593SDavid van Moolenbroek gcp->attr ^= GRID_ATTR_REVERSE;
900eda6f593SDavid van Moolenbroek
901eda6f593SDavid van Moolenbroek if (grid_compare(c->status.grid, old_status.grid) == 0) {
902eda6f593SDavid van Moolenbroek screen_free(&old_status);
903eda6f593SDavid van Moolenbroek return (0);
904eda6f593SDavid van Moolenbroek }
905eda6f593SDavid van Moolenbroek screen_free(&old_status);
906eda6f593SDavid van Moolenbroek return (1);
907eda6f593SDavid van Moolenbroek }
908eda6f593SDavid van Moolenbroek
909eda6f593SDavid van Moolenbroek /* Handle keys in prompt. */
910eda6f593SDavid van Moolenbroek void
status_prompt_key(struct client * c,int key)911eda6f593SDavid van Moolenbroek status_prompt_key(struct client *c, int key)
912eda6f593SDavid van Moolenbroek {
913*0a6a1f1dSLionel Sambuc struct session *sess = c->session;
914*0a6a1f1dSLionel Sambuc struct options *oo = &sess->options;
915eda6f593SDavid van Moolenbroek struct paste_buffer *pb;
916eda6f593SDavid van Moolenbroek char *s, *first, *last, word[64], swapc;
917eda6f593SDavid van Moolenbroek const char *histstr;
918*0a6a1f1dSLionel Sambuc const char *wsep = NULL;
919eda6f593SDavid van Moolenbroek u_char ch;
920eda6f593SDavid van Moolenbroek size_t size, n, off, idx;
921eda6f593SDavid van Moolenbroek
922eda6f593SDavid van Moolenbroek size = strlen(c->prompt_buffer);
923*0a6a1f1dSLionel Sambuc switch (mode_key_lookup(&c->prompt_mdata, key, NULL)) {
924eda6f593SDavid van Moolenbroek case MODEKEYEDIT_CURSORLEFT:
925eda6f593SDavid van Moolenbroek if (c->prompt_index > 0) {
926eda6f593SDavid van Moolenbroek c->prompt_index--;
927eda6f593SDavid van Moolenbroek c->flags |= CLIENT_STATUS;
928eda6f593SDavid van Moolenbroek }
929eda6f593SDavid van Moolenbroek break;
930*0a6a1f1dSLionel Sambuc case MODEKEYEDIT_SWITCHMODE:
931*0a6a1f1dSLionel Sambuc c->flags |= CLIENT_STATUS;
932*0a6a1f1dSLionel Sambuc break;
933eda6f593SDavid van Moolenbroek case MODEKEYEDIT_SWITCHMODEAPPEND:
934*0a6a1f1dSLionel Sambuc c->flags |= CLIENT_STATUS;
935*0a6a1f1dSLionel Sambuc /* FALLTHROUGH */
936eda6f593SDavid van Moolenbroek case MODEKEYEDIT_CURSORRIGHT:
937eda6f593SDavid van Moolenbroek if (c->prompt_index < size) {
938eda6f593SDavid van Moolenbroek c->prompt_index++;
939eda6f593SDavid van Moolenbroek c->flags |= CLIENT_STATUS;
940eda6f593SDavid van Moolenbroek }
941eda6f593SDavid van Moolenbroek break;
942*0a6a1f1dSLionel Sambuc case MODEKEYEDIT_SWITCHMODEBEGINLINE:
943*0a6a1f1dSLionel Sambuc c->flags |= CLIENT_STATUS;
944*0a6a1f1dSLionel Sambuc /* FALLTHROUGH */
945eda6f593SDavid van Moolenbroek case MODEKEYEDIT_STARTOFLINE:
946eda6f593SDavid van Moolenbroek if (c->prompt_index != 0) {
947eda6f593SDavid van Moolenbroek c->prompt_index = 0;
948eda6f593SDavid van Moolenbroek c->flags |= CLIENT_STATUS;
949eda6f593SDavid van Moolenbroek }
950eda6f593SDavid van Moolenbroek break;
951*0a6a1f1dSLionel Sambuc case MODEKEYEDIT_SWITCHMODEAPPENDLINE:
952*0a6a1f1dSLionel Sambuc c->flags |= CLIENT_STATUS;
953*0a6a1f1dSLionel Sambuc /* FALLTHROUGH */
954eda6f593SDavid van Moolenbroek case MODEKEYEDIT_ENDOFLINE:
955eda6f593SDavid van Moolenbroek if (c->prompt_index != size) {
956eda6f593SDavid van Moolenbroek c->prompt_index = size;
957eda6f593SDavid van Moolenbroek c->flags |= CLIENT_STATUS;
958eda6f593SDavid van Moolenbroek }
959eda6f593SDavid van Moolenbroek break;
960eda6f593SDavid van Moolenbroek case MODEKEYEDIT_COMPLETE:
961eda6f593SDavid van Moolenbroek if (*c->prompt_buffer == '\0')
962eda6f593SDavid van Moolenbroek break;
963eda6f593SDavid van Moolenbroek
964eda6f593SDavid van Moolenbroek idx = c->prompt_index;
965eda6f593SDavid van Moolenbroek if (idx != 0)
966eda6f593SDavid van Moolenbroek idx--;
967eda6f593SDavid van Moolenbroek
968eda6f593SDavid van Moolenbroek /* Find the word we are in. */
969eda6f593SDavid van Moolenbroek first = c->prompt_buffer + idx;
970eda6f593SDavid van Moolenbroek while (first > c->prompt_buffer && *first != ' ')
971eda6f593SDavid van Moolenbroek first--;
972eda6f593SDavid van Moolenbroek while (*first == ' ')
973eda6f593SDavid van Moolenbroek first++;
974eda6f593SDavid van Moolenbroek last = c->prompt_buffer + idx;
975eda6f593SDavid van Moolenbroek while (*last != '\0' && *last != ' ')
976eda6f593SDavid van Moolenbroek last++;
977eda6f593SDavid van Moolenbroek while (*last == ' ')
978eda6f593SDavid van Moolenbroek last--;
979eda6f593SDavid van Moolenbroek if (*last != '\0')
980eda6f593SDavid van Moolenbroek last++;
981eda6f593SDavid van Moolenbroek if (last <= first ||
982eda6f593SDavid van Moolenbroek ((size_t) (last - first)) > (sizeof word) - 1)
983eda6f593SDavid van Moolenbroek break;
984eda6f593SDavid van Moolenbroek memcpy(word, first, last - first);
985eda6f593SDavid van Moolenbroek word[last - first] = '\0';
986eda6f593SDavid van Moolenbroek
987eda6f593SDavid van Moolenbroek /* And try to complete it. */
988eda6f593SDavid van Moolenbroek if ((s = status_prompt_complete(word)) == NULL)
989eda6f593SDavid van Moolenbroek break;
990eda6f593SDavid van Moolenbroek
991eda6f593SDavid van Moolenbroek /* Trim out word. */
992eda6f593SDavid van Moolenbroek n = size - (last - c->prompt_buffer) + 1; /* with \0 */
993eda6f593SDavid van Moolenbroek memmove(first, last, n);
994eda6f593SDavid van Moolenbroek size -= last - first;
995eda6f593SDavid van Moolenbroek
996eda6f593SDavid van Moolenbroek /* Insert the new word. */
997eda6f593SDavid van Moolenbroek size += strlen(s);
998eda6f593SDavid van Moolenbroek off = first - c->prompt_buffer;
999eda6f593SDavid van Moolenbroek c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 1);
1000eda6f593SDavid van Moolenbroek first = c->prompt_buffer + off;
1001eda6f593SDavid van Moolenbroek memmove(first + strlen(s), first, n);
1002eda6f593SDavid van Moolenbroek memcpy(first, s, strlen(s));
1003eda6f593SDavid van Moolenbroek
1004eda6f593SDavid van Moolenbroek c->prompt_index = (first - c->prompt_buffer) + strlen(s);
1005*0a6a1f1dSLionel Sambuc free(s);
1006eda6f593SDavid van Moolenbroek
1007eda6f593SDavid van Moolenbroek c->flags |= CLIENT_STATUS;
1008eda6f593SDavid van Moolenbroek break;
1009eda6f593SDavid van Moolenbroek case MODEKEYEDIT_BACKSPACE:
1010eda6f593SDavid van Moolenbroek if (c->prompt_index != 0) {
1011eda6f593SDavid van Moolenbroek if (c->prompt_index == size)
1012eda6f593SDavid van Moolenbroek c->prompt_buffer[--c->prompt_index] = '\0';
1013eda6f593SDavid van Moolenbroek else {
1014eda6f593SDavid van Moolenbroek memmove(c->prompt_buffer + c->prompt_index - 1,
1015eda6f593SDavid van Moolenbroek c->prompt_buffer + c->prompt_index,
1016eda6f593SDavid van Moolenbroek size + 1 - c->prompt_index);
1017eda6f593SDavid van Moolenbroek c->prompt_index--;
1018eda6f593SDavid van Moolenbroek }
1019eda6f593SDavid van Moolenbroek c->flags |= CLIENT_STATUS;
1020eda6f593SDavid van Moolenbroek }
1021eda6f593SDavid van Moolenbroek break;
1022eda6f593SDavid van Moolenbroek case MODEKEYEDIT_DELETE:
1023*0a6a1f1dSLionel Sambuc case MODEKEYEDIT_SWITCHMODESUBSTITUTE:
1024eda6f593SDavid van Moolenbroek if (c->prompt_index != size) {
1025eda6f593SDavid van Moolenbroek memmove(c->prompt_buffer + c->prompt_index,
1026eda6f593SDavid van Moolenbroek c->prompt_buffer + c->prompt_index + 1,
1027eda6f593SDavid van Moolenbroek size + 1 - c->prompt_index);
1028eda6f593SDavid van Moolenbroek c->flags |= CLIENT_STATUS;
1029eda6f593SDavid van Moolenbroek }
1030eda6f593SDavid van Moolenbroek break;
1031eda6f593SDavid van Moolenbroek case MODEKEYEDIT_DELETELINE:
1032*0a6a1f1dSLionel Sambuc case MODEKEYEDIT_SWITCHMODESUBSTITUTELINE:
1033eda6f593SDavid van Moolenbroek *c->prompt_buffer = '\0';
1034eda6f593SDavid van Moolenbroek c->prompt_index = 0;
1035eda6f593SDavid van Moolenbroek c->flags |= CLIENT_STATUS;
1036eda6f593SDavid van Moolenbroek break;
1037eda6f593SDavid van Moolenbroek case MODEKEYEDIT_DELETETOENDOFLINE:
1038*0a6a1f1dSLionel Sambuc case MODEKEYEDIT_SWITCHMODECHANGELINE:
1039eda6f593SDavid van Moolenbroek if (c->prompt_index < size) {
1040eda6f593SDavid van Moolenbroek c->prompt_buffer[c->prompt_index] = '\0';
1041eda6f593SDavid van Moolenbroek c->flags |= CLIENT_STATUS;
1042eda6f593SDavid van Moolenbroek }
1043eda6f593SDavid van Moolenbroek break;
1044*0a6a1f1dSLionel Sambuc case MODEKEYEDIT_DELETEWORD:
1045*0a6a1f1dSLionel Sambuc wsep = options_get_string(oo, "word-separators");
1046*0a6a1f1dSLionel Sambuc idx = c->prompt_index;
1047*0a6a1f1dSLionel Sambuc
1048*0a6a1f1dSLionel Sambuc /* Find a non-separator. */
1049*0a6a1f1dSLionel Sambuc while (idx != 0) {
1050*0a6a1f1dSLionel Sambuc idx--;
1051*0a6a1f1dSLionel Sambuc if (!strchr(wsep, c->prompt_buffer[idx]))
1052*0a6a1f1dSLionel Sambuc break;
1053*0a6a1f1dSLionel Sambuc }
1054*0a6a1f1dSLionel Sambuc
1055*0a6a1f1dSLionel Sambuc /* Find the separator at the beginning of the word. */
1056*0a6a1f1dSLionel Sambuc while (idx != 0) {
1057*0a6a1f1dSLionel Sambuc idx--;
1058*0a6a1f1dSLionel Sambuc if (strchr(wsep, c->prompt_buffer[idx])) {
1059*0a6a1f1dSLionel Sambuc /* Go back to the word. */
1060*0a6a1f1dSLionel Sambuc idx++;
1061*0a6a1f1dSLionel Sambuc break;
1062*0a6a1f1dSLionel Sambuc }
1063*0a6a1f1dSLionel Sambuc }
1064*0a6a1f1dSLionel Sambuc
1065*0a6a1f1dSLionel Sambuc memmove(c->prompt_buffer + idx,
1066*0a6a1f1dSLionel Sambuc c->prompt_buffer + c->prompt_index,
1067*0a6a1f1dSLionel Sambuc size + 1 - c->prompt_index);
1068*0a6a1f1dSLionel Sambuc memset(c->prompt_buffer + size - (c->prompt_index - idx),
1069*0a6a1f1dSLionel Sambuc '\0', c->prompt_index - idx);
1070*0a6a1f1dSLionel Sambuc c->prompt_index = idx;
1071*0a6a1f1dSLionel Sambuc c->flags |= CLIENT_STATUS;
1072*0a6a1f1dSLionel Sambuc break;
1073*0a6a1f1dSLionel Sambuc case MODEKEYEDIT_NEXTSPACE:
1074*0a6a1f1dSLionel Sambuc wsep = " ";
1075*0a6a1f1dSLionel Sambuc /* FALLTHROUGH */
1076*0a6a1f1dSLionel Sambuc case MODEKEYEDIT_NEXTWORD:
1077*0a6a1f1dSLionel Sambuc if (wsep == NULL)
1078*0a6a1f1dSLionel Sambuc wsep = options_get_string(oo, "word-separators");
1079*0a6a1f1dSLionel Sambuc
1080*0a6a1f1dSLionel Sambuc /* Find a separator. */
1081*0a6a1f1dSLionel Sambuc while (c->prompt_index != size) {
1082*0a6a1f1dSLionel Sambuc c->prompt_index++;
1083*0a6a1f1dSLionel Sambuc if (strchr(wsep, c->prompt_buffer[c->prompt_index]))
1084*0a6a1f1dSLionel Sambuc break;
1085*0a6a1f1dSLionel Sambuc }
1086*0a6a1f1dSLionel Sambuc
1087*0a6a1f1dSLionel Sambuc /* Find the word right after the separation. */
1088*0a6a1f1dSLionel Sambuc while (c->prompt_index != size) {
1089*0a6a1f1dSLionel Sambuc c->prompt_index++;
1090*0a6a1f1dSLionel Sambuc if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
1091*0a6a1f1dSLionel Sambuc break;
1092*0a6a1f1dSLionel Sambuc }
1093*0a6a1f1dSLionel Sambuc
1094*0a6a1f1dSLionel Sambuc c->flags |= CLIENT_STATUS;
1095*0a6a1f1dSLionel Sambuc break;
1096*0a6a1f1dSLionel Sambuc case MODEKEYEDIT_NEXTSPACEEND:
1097*0a6a1f1dSLionel Sambuc wsep = " ";
1098*0a6a1f1dSLionel Sambuc /* FALLTHROUGH */
1099*0a6a1f1dSLionel Sambuc case MODEKEYEDIT_NEXTWORDEND:
1100*0a6a1f1dSLionel Sambuc if (wsep == NULL)
1101*0a6a1f1dSLionel Sambuc wsep = options_get_string(oo, "word-separators");
1102*0a6a1f1dSLionel Sambuc
1103*0a6a1f1dSLionel Sambuc /* Find a word. */
1104*0a6a1f1dSLionel Sambuc while (c->prompt_index != size) {
1105*0a6a1f1dSLionel Sambuc c->prompt_index++;
1106*0a6a1f1dSLionel Sambuc if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
1107*0a6a1f1dSLionel Sambuc break;
1108*0a6a1f1dSLionel Sambuc }
1109*0a6a1f1dSLionel Sambuc
1110*0a6a1f1dSLionel Sambuc /* Find the separator at the end of the word. */
1111*0a6a1f1dSLionel Sambuc while (c->prompt_index != size) {
1112*0a6a1f1dSLionel Sambuc c->prompt_index++;
1113*0a6a1f1dSLionel Sambuc if (strchr(wsep, c->prompt_buffer[c->prompt_index]))
1114*0a6a1f1dSLionel Sambuc break;
1115*0a6a1f1dSLionel Sambuc }
1116*0a6a1f1dSLionel Sambuc
1117*0a6a1f1dSLionel Sambuc /* Back up to the end-of-word like vi. */
1118*0a6a1f1dSLionel Sambuc if (options_get_number(oo, "status-keys") == MODEKEY_VI &&
1119*0a6a1f1dSLionel Sambuc c->prompt_index != 0)
1120*0a6a1f1dSLionel Sambuc c->prompt_index--;
1121*0a6a1f1dSLionel Sambuc
1122*0a6a1f1dSLionel Sambuc c->flags |= CLIENT_STATUS;
1123*0a6a1f1dSLionel Sambuc break;
1124*0a6a1f1dSLionel Sambuc case MODEKEYEDIT_PREVIOUSSPACE:
1125*0a6a1f1dSLionel Sambuc wsep = " ";
1126*0a6a1f1dSLionel Sambuc /* FALLTHROUGH */
1127*0a6a1f1dSLionel Sambuc case MODEKEYEDIT_PREVIOUSWORD:
1128*0a6a1f1dSLionel Sambuc if (wsep == NULL)
1129*0a6a1f1dSLionel Sambuc wsep = options_get_string(oo, "word-separators");
1130*0a6a1f1dSLionel Sambuc
1131*0a6a1f1dSLionel Sambuc /* Find a non-separator. */
1132*0a6a1f1dSLionel Sambuc while (c->prompt_index != 0) {
1133*0a6a1f1dSLionel Sambuc c->prompt_index--;
1134*0a6a1f1dSLionel Sambuc if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
1135*0a6a1f1dSLionel Sambuc break;
1136*0a6a1f1dSLionel Sambuc }
1137*0a6a1f1dSLionel Sambuc
1138*0a6a1f1dSLionel Sambuc /* Find the separator at the beginning of the word. */
1139*0a6a1f1dSLionel Sambuc while (c->prompt_index != 0) {
1140*0a6a1f1dSLionel Sambuc c->prompt_index--;
1141*0a6a1f1dSLionel Sambuc if (strchr(wsep, c->prompt_buffer[c->prompt_index])) {
1142*0a6a1f1dSLionel Sambuc /* Go back to the word. */
1143*0a6a1f1dSLionel Sambuc c->prompt_index++;
1144*0a6a1f1dSLionel Sambuc break;
1145*0a6a1f1dSLionel Sambuc }
1146*0a6a1f1dSLionel Sambuc }
1147*0a6a1f1dSLionel Sambuc
1148*0a6a1f1dSLionel Sambuc c->flags |= CLIENT_STATUS;
1149*0a6a1f1dSLionel Sambuc break;
1150eda6f593SDavid van Moolenbroek case MODEKEYEDIT_HISTORYUP:
1151eda6f593SDavid van Moolenbroek histstr = status_prompt_up_history(&c->prompt_hindex);
1152eda6f593SDavid van Moolenbroek if (histstr == NULL)
1153eda6f593SDavid van Moolenbroek break;
1154*0a6a1f1dSLionel Sambuc free(c->prompt_buffer);
1155eda6f593SDavid van Moolenbroek c->prompt_buffer = xstrdup(histstr);
1156eda6f593SDavid van Moolenbroek c->prompt_index = strlen(c->prompt_buffer);
1157eda6f593SDavid van Moolenbroek c->flags |= CLIENT_STATUS;
1158eda6f593SDavid van Moolenbroek break;
1159eda6f593SDavid van Moolenbroek case MODEKEYEDIT_HISTORYDOWN:
1160eda6f593SDavid van Moolenbroek histstr = status_prompt_down_history(&c->prompt_hindex);
1161eda6f593SDavid van Moolenbroek if (histstr == NULL)
1162eda6f593SDavid van Moolenbroek break;
1163*0a6a1f1dSLionel Sambuc free(c->prompt_buffer);
1164eda6f593SDavid van Moolenbroek c->prompt_buffer = xstrdup(histstr);
1165eda6f593SDavid van Moolenbroek c->prompt_index = strlen(c->prompt_buffer);
1166eda6f593SDavid van Moolenbroek c->flags |= CLIENT_STATUS;
1167eda6f593SDavid van Moolenbroek break;
1168eda6f593SDavid van Moolenbroek case MODEKEYEDIT_PASTE:
1169eda6f593SDavid van Moolenbroek if ((pb = paste_get_top(&global_buffers)) == NULL)
1170eda6f593SDavid van Moolenbroek break;
1171eda6f593SDavid van Moolenbroek for (n = 0; n < pb->size; n++) {
1172eda6f593SDavid van Moolenbroek ch = (u_char) pb->data[n];
1173eda6f593SDavid van Moolenbroek if (ch < 32 || ch == 127)
1174eda6f593SDavid van Moolenbroek break;
1175eda6f593SDavid van Moolenbroek }
1176eda6f593SDavid van Moolenbroek
1177eda6f593SDavid van Moolenbroek c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + n + 1);
1178eda6f593SDavid van Moolenbroek if (c->prompt_index == size) {
1179eda6f593SDavid van Moolenbroek memcpy(c->prompt_buffer + c->prompt_index, pb->data, n);
1180eda6f593SDavid van Moolenbroek c->prompt_index += n;
1181eda6f593SDavid van Moolenbroek c->prompt_buffer[c->prompt_index] = '\0';
1182eda6f593SDavid van Moolenbroek } else {
1183eda6f593SDavid van Moolenbroek memmove(c->prompt_buffer + c->prompt_index + n,
1184eda6f593SDavid van Moolenbroek c->prompt_buffer + c->prompt_index,
1185eda6f593SDavid van Moolenbroek size + 1 - c->prompt_index);
1186eda6f593SDavid van Moolenbroek memcpy(c->prompt_buffer + c->prompt_index, pb->data, n);
1187eda6f593SDavid van Moolenbroek c->prompt_index += n;
1188eda6f593SDavid van Moolenbroek }
1189eda6f593SDavid van Moolenbroek
1190eda6f593SDavid van Moolenbroek c->flags |= CLIENT_STATUS;
1191eda6f593SDavid van Moolenbroek break;
1192eda6f593SDavid van Moolenbroek case MODEKEYEDIT_TRANSPOSECHARS:
1193eda6f593SDavid van Moolenbroek idx = c->prompt_index;
1194eda6f593SDavid van Moolenbroek if (idx < size)
1195eda6f593SDavid van Moolenbroek idx++;
1196eda6f593SDavid van Moolenbroek if (idx >= 2) {
1197eda6f593SDavid van Moolenbroek swapc = c->prompt_buffer[idx - 2];
1198eda6f593SDavid van Moolenbroek c->prompt_buffer[idx - 2] = c->prompt_buffer[idx - 1];
1199eda6f593SDavid van Moolenbroek c->prompt_buffer[idx - 1] = swapc;
1200eda6f593SDavid van Moolenbroek c->prompt_index = idx;
1201eda6f593SDavid van Moolenbroek c->flags |= CLIENT_STATUS;
1202eda6f593SDavid van Moolenbroek }
1203eda6f593SDavid van Moolenbroek break;
1204eda6f593SDavid van Moolenbroek case MODEKEYEDIT_ENTER:
1205eda6f593SDavid van Moolenbroek if (*c->prompt_buffer != '\0')
1206eda6f593SDavid van Moolenbroek status_prompt_add_history(c->prompt_buffer);
1207eda6f593SDavid van Moolenbroek if (c->prompt_callbackfn(c->prompt_data, c->prompt_buffer) == 0)
1208eda6f593SDavid van Moolenbroek status_prompt_clear(c);
1209eda6f593SDavid van Moolenbroek break;
1210eda6f593SDavid van Moolenbroek case MODEKEYEDIT_CANCEL:
1211eda6f593SDavid van Moolenbroek if (c->prompt_callbackfn(c->prompt_data, NULL) == 0)
1212eda6f593SDavid van Moolenbroek status_prompt_clear(c);
1213eda6f593SDavid van Moolenbroek break;
1214eda6f593SDavid van Moolenbroek case MODEKEY_OTHER:
1215eda6f593SDavid van Moolenbroek if ((key & 0xff00) != 0 || key < 32 || key == 127)
1216eda6f593SDavid van Moolenbroek break;
1217eda6f593SDavid van Moolenbroek c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 2);
1218eda6f593SDavid van Moolenbroek
1219eda6f593SDavid van Moolenbroek if (c->prompt_index == size) {
1220eda6f593SDavid van Moolenbroek c->prompt_buffer[c->prompt_index++] = key;
1221eda6f593SDavid van Moolenbroek c->prompt_buffer[c->prompt_index] = '\0';
1222eda6f593SDavid van Moolenbroek } else {
1223eda6f593SDavid van Moolenbroek memmove(c->prompt_buffer + c->prompt_index + 1,
1224eda6f593SDavid van Moolenbroek c->prompt_buffer + c->prompt_index,
1225eda6f593SDavid van Moolenbroek size + 1 - c->prompt_index);
1226eda6f593SDavid van Moolenbroek c->prompt_buffer[c->prompt_index++] = key;
1227eda6f593SDavid van Moolenbroek }
1228eda6f593SDavid van Moolenbroek
1229eda6f593SDavid van Moolenbroek if (c->prompt_flags & PROMPT_SINGLE) {
1230eda6f593SDavid van Moolenbroek if (c->prompt_callbackfn(
1231eda6f593SDavid van Moolenbroek c->prompt_data, c->prompt_buffer) == 0)
1232eda6f593SDavid van Moolenbroek status_prompt_clear(c);
1233eda6f593SDavid van Moolenbroek }
1234eda6f593SDavid van Moolenbroek
1235eda6f593SDavid van Moolenbroek c->flags |= CLIENT_STATUS;
1236eda6f593SDavid van Moolenbroek break;
1237eda6f593SDavid van Moolenbroek default:
1238eda6f593SDavid van Moolenbroek break;
1239eda6f593SDavid van Moolenbroek }
1240eda6f593SDavid van Moolenbroek }
1241eda6f593SDavid van Moolenbroek
1242eda6f593SDavid van Moolenbroek /* Get previous line from the history. */
1243eda6f593SDavid van Moolenbroek const char *
status_prompt_up_history(u_int * idx)1244eda6f593SDavid van Moolenbroek status_prompt_up_history(u_int *idx)
1245eda6f593SDavid van Moolenbroek {
1246eda6f593SDavid van Moolenbroek u_int size;
1247eda6f593SDavid van Moolenbroek
1248eda6f593SDavid van Moolenbroek /*
1249eda6f593SDavid van Moolenbroek * History runs from 0 to size - 1.
1250eda6f593SDavid van Moolenbroek *
1251eda6f593SDavid van Moolenbroek * Index is from 0 to size. Zero is empty.
1252eda6f593SDavid van Moolenbroek */
1253eda6f593SDavid van Moolenbroek
1254eda6f593SDavid van Moolenbroek size = ARRAY_LENGTH(&status_prompt_history);
1255eda6f593SDavid van Moolenbroek if (size == 0 || *idx == size)
1256eda6f593SDavid van Moolenbroek return (NULL);
1257eda6f593SDavid van Moolenbroek (*idx)++;
1258eda6f593SDavid van Moolenbroek return (ARRAY_ITEM(&status_prompt_history, size - *idx));
1259eda6f593SDavid van Moolenbroek }
1260eda6f593SDavid van Moolenbroek
1261eda6f593SDavid van Moolenbroek /* Get next line from the history. */
1262eda6f593SDavid van Moolenbroek const char *
status_prompt_down_history(u_int * idx)1263eda6f593SDavid van Moolenbroek status_prompt_down_history(u_int *idx)
1264eda6f593SDavid van Moolenbroek {
1265eda6f593SDavid van Moolenbroek u_int size;
1266eda6f593SDavid van Moolenbroek
1267eda6f593SDavid van Moolenbroek size = ARRAY_LENGTH(&status_prompt_history);
1268eda6f593SDavid van Moolenbroek if (size == 0 || *idx == 0)
1269eda6f593SDavid van Moolenbroek return ("");
1270eda6f593SDavid van Moolenbroek (*idx)--;
1271eda6f593SDavid van Moolenbroek if (*idx == 0)
1272eda6f593SDavid van Moolenbroek return ("");
1273eda6f593SDavid van Moolenbroek return (ARRAY_ITEM(&status_prompt_history, size - *idx));
1274eda6f593SDavid van Moolenbroek }
1275eda6f593SDavid van Moolenbroek
1276eda6f593SDavid van Moolenbroek /* Add line to the history. */
1277eda6f593SDavid van Moolenbroek void
status_prompt_add_history(const char * line)1278eda6f593SDavid van Moolenbroek status_prompt_add_history(const char *line)
1279eda6f593SDavid van Moolenbroek {
1280eda6f593SDavid van Moolenbroek u_int size;
1281eda6f593SDavid van Moolenbroek
1282eda6f593SDavid van Moolenbroek size = ARRAY_LENGTH(&status_prompt_history);
1283eda6f593SDavid van Moolenbroek if (size > 0 && strcmp(ARRAY_LAST(&status_prompt_history), line) == 0)
1284eda6f593SDavid van Moolenbroek return;
1285eda6f593SDavid van Moolenbroek
1286eda6f593SDavid van Moolenbroek if (size == PROMPT_HISTORY) {
1287*0a6a1f1dSLionel Sambuc free(ARRAY_FIRST(&status_prompt_history));
1288eda6f593SDavid van Moolenbroek ARRAY_REMOVE(&status_prompt_history, 0);
1289eda6f593SDavid van Moolenbroek }
1290eda6f593SDavid van Moolenbroek
1291eda6f593SDavid van Moolenbroek ARRAY_ADD(&status_prompt_history, xstrdup(line));
1292eda6f593SDavid van Moolenbroek }
1293eda6f593SDavid van Moolenbroek
1294eda6f593SDavid van Moolenbroek /* Complete word. */
1295eda6f593SDavid van Moolenbroek char *
status_prompt_complete(const char * s)1296eda6f593SDavid van Moolenbroek status_prompt_complete(const char *s)
1297eda6f593SDavid van Moolenbroek {
1298eda6f593SDavid van Moolenbroek const struct cmd_entry **cmdent;
1299eda6f593SDavid van Moolenbroek const struct options_table_entry *oe;
1300eda6f593SDavid van Moolenbroek ARRAY_DECL(, const char *) list;
1301eda6f593SDavid van Moolenbroek char *prefix, *s2;
1302eda6f593SDavid van Moolenbroek u_int i;
1303eda6f593SDavid van Moolenbroek size_t j;
1304eda6f593SDavid van Moolenbroek
1305eda6f593SDavid van Moolenbroek if (*s == '\0')
1306eda6f593SDavid van Moolenbroek return (NULL);
1307eda6f593SDavid van Moolenbroek
1308eda6f593SDavid van Moolenbroek /* First, build a list of all the possible matches. */
1309eda6f593SDavid van Moolenbroek ARRAY_INIT(&list);
1310eda6f593SDavid van Moolenbroek for (cmdent = cmd_table; *cmdent != NULL; cmdent++) {
1311eda6f593SDavid van Moolenbroek if (strncmp((*cmdent)->name, s, strlen(s)) == 0)
1312eda6f593SDavid van Moolenbroek ARRAY_ADD(&list, (*cmdent)->name);
1313eda6f593SDavid van Moolenbroek }
1314eda6f593SDavid van Moolenbroek for (oe = server_options_table; oe->name != NULL; oe++) {
1315eda6f593SDavid van Moolenbroek if (strncmp(oe->name, s, strlen(s)) == 0)
1316eda6f593SDavid van Moolenbroek ARRAY_ADD(&list, oe->name);
1317eda6f593SDavid van Moolenbroek }
1318eda6f593SDavid van Moolenbroek for (oe = session_options_table; oe->name != NULL; oe++) {
1319eda6f593SDavid van Moolenbroek if (strncmp(oe->name, s, strlen(s)) == 0)
1320eda6f593SDavid van Moolenbroek ARRAY_ADD(&list, oe->name);
1321eda6f593SDavid van Moolenbroek }
1322eda6f593SDavid van Moolenbroek for (oe = window_options_table; oe->name != NULL; oe++) {
1323eda6f593SDavid van Moolenbroek if (strncmp(oe->name, s, strlen(s)) == 0)
1324eda6f593SDavid van Moolenbroek ARRAY_ADD(&list, oe->name);
1325eda6f593SDavid van Moolenbroek }
1326eda6f593SDavid van Moolenbroek
1327eda6f593SDavid van Moolenbroek /* If none, bail now. */
1328eda6f593SDavid van Moolenbroek if (ARRAY_LENGTH(&list) == 0) {
1329eda6f593SDavid van Moolenbroek ARRAY_FREE(&list);
1330eda6f593SDavid van Moolenbroek return (NULL);
1331eda6f593SDavid van Moolenbroek }
1332eda6f593SDavid van Moolenbroek
1333eda6f593SDavid van Moolenbroek /* If an exact match, return it, with a trailing space. */
1334eda6f593SDavid van Moolenbroek if (ARRAY_LENGTH(&list) == 1) {
1335eda6f593SDavid van Moolenbroek xasprintf(&s2, "%s ", ARRAY_FIRST(&list));
1336eda6f593SDavid van Moolenbroek ARRAY_FREE(&list);
1337eda6f593SDavid van Moolenbroek return (s2);
1338eda6f593SDavid van Moolenbroek }
1339eda6f593SDavid van Moolenbroek
1340eda6f593SDavid van Moolenbroek /* Now loop through the list and find the longest common prefix. */
1341eda6f593SDavid van Moolenbroek prefix = xstrdup(ARRAY_FIRST(&list));
1342eda6f593SDavid van Moolenbroek for (i = 1; i < ARRAY_LENGTH(&list); i++) {
1343eda6f593SDavid van Moolenbroek s = ARRAY_ITEM(&list, i);
1344eda6f593SDavid van Moolenbroek
1345eda6f593SDavid van Moolenbroek j = strlen(s);
1346eda6f593SDavid van Moolenbroek if (j > strlen(prefix))
1347eda6f593SDavid van Moolenbroek j = strlen(prefix);
1348eda6f593SDavid van Moolenbroek for (; j > 0; j--) {
1349eda6f593SDavid van Moolenbroek if (prefix[j - 1] != s[j - 1])
1350eda6f593SDavid van Moolenbroek prefix[j - 1] = '\0';
1351eda6f593SDavid van Moolenbroek }
1352eda6f593SDavid van Moolenbroek }
1353eda6f593SDavid van Moolenbroek
1354eda6f593SDavid van Moolenbroek ARRAY_FREE(&list);
1355eda6f593SDavid van Moolenbroek return (prefix);
1356eda6f593SDavid van Moolenbroek }
1357