1 /* $OpenBSD$ */
2 
3 /*
4  * Copyright (c) 2012 George Nachman <tmux@georgester.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 struct notify_entry {
27           const char                    *name;
28           struct cmd_find_state          fs;
29           struct format_tree  *formats;
30 
31           struct client                 *client;
32           struct session                *session;
33           struct window                 *window;
34           int                            pane;
35           const char                    *pbname;
36 };
37 
38 static struct cmdq_item *
notify_insert_one_hook(struct cmdq_item * item,struct notify_entry * ne,struct cmd_list * cmdlist,struct cmdq_state * state)39 notify_insert_one_hook(struct cmdq_item *item, struct notify_entry *ne,
40     struct cmd_list *cmdlist, struct cmdq_state *state)
41 {
42           struct cmdq_item    *new_item;
43           char                          *s;
44 
45           if (cmdlist == NULL)
46                     return (item);
47           if (log_get_level() != 0) {
48                     s = cmd_list_print(cmdlist, 0);
49                     log_debug("%s: hook %s is: %s", __func__, ne->name, s);
50                     free(s);
51           }
52           new_item = cmdq_get_command(cmdlist, state);
53           return (cmdq_insert_after(item, new_item));
54 }
55 
56 static void
notify_insert_hook(struct cmdq_item * item,struct notify_entry * ne)57 notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne)
58 {
59           struct cmd_find_state                    fs;
60           struct options                          *oo;
61           struct cmdq_state             *state;
62           struct options_entry                    *o;
63           struct options_array_item     *a;
64           struct cmd_list                         *cmdlist;
65           const char                              *value;
66           struct cmd_parse_result                 *pr;
67 
68           log_debug("%s: inserting hook %s", __func__, ne->name);
69 
70           cmd_find_clear_state(&fs, 0);
71           if (cmd_find_empty_state(&ne->fs) || !cmd_find_valid_state(&ne->fs))
72                     cmd_find_from_nothing(&fs, 0);
73           else
74                     cmd_find_copy_state(&fs, &ne->fs);
75 
76           if (fs.s == NULL)
77                     oo = global_s_options;
78           else
79                     oo = fs.s->options;
80           o = options_get(oo, ne->name);
81           if (o == NULL && fs.wp != NULL) {
82                     oo = fs.wp->options;
83                     o = options_get(oo, ne->name);
84           }
85           if (o == NULL && fs.wl != NULL) {
86                     oo = fs.wl->window->options;
87                     o = options_get(oo, ne->name);
88           }
89           if (o == NULL) {
90                     log_debug("%s: hook %s not found", __func__, ne->name);
91                     return;
92           }
93 
94           state = cmdq_new_state(&fs, NULL, CMDQ_STATE_NOHOOKS);
95           cmdq_add_formats(state, ne->formats);
96 
97           if (*ne->name == '@') {
98                     value = options_get_string(oo, ne->name);
99                     pr = cmd_parse_from_string(value, NULL);
100                     switch (pr->status) {
101                     case CMD_PARSE_ERROR:
102                               log_debug("%s: can't parse hook %s: %s", __func__,
103                                   ne->name, pr->error);
104                               free(pr->error);
105                               break;
106                     case CMD_PARSE_SUCCESS:
107                               notify_insert_one_hook(item, ne, pr->cmdlist, state);
108                               break;
109                     }
110           } else {
111                     a = options_array_first(o);
112                     while (a != NULL) {
113                               cmdlist = options_array_item_value(a)->cmdlist;
114                               item = notify_insert_one_hook(item, ne, cmdlist, state);
115                               a = options_array_next(a);
116                     }
117           }
118 
119           cmdq_free_state(state);
120 }
121 
122 static enum cmd_retval
notify_callback(struct cmdq_item * item,void * data)123 notify_callback(struct cmdq_item *item, void *data)
124 {
125           struct notify_entry *ne = data;
126 
127           log_debug("%s: %s", __func__, ne->name);
128 
129           if (strcmp(ne->name, "pane-mode-changed") == 0)
130                     control_notify_pane_mode_changed(ne->pane);
131           if (strcmp(ne->name, "window-layout-changed") == 0)
132                     control_notify_window_layout_changed(ne->window);
133           if (strcmp(ne->name, "window-pane-changed") == 0)
134                     control_notify_window_pane_changed(ne->window);
135           if (strcmp(ne->name, "window-unlinked") == 0)
136                     control_notify_window_unlinked(ne->session, ne->window);
137           if (strcmp(ne->name, "window-linked") == 0)
138                     control_notify_window_linked(ne->session, ne->window);
139           if (strcmp(ne->name, "window-renamed") == 0)
140                     control_notify_window_renamed(ne->window);
141           if (strcmp(ne->name, "client-session-changed") == 0)
142                     control_notify_client_session_changed(ne->client);
143           if (strcmp(ne->name, "client-detached") == 0)
144                     control_notify_client_detached(ne->client);
145           if (strcmp(ne->name, "session-renamed") == 0)
146                     control_notify_session_renamed(ne->session);
147           if (strcmp(ne->name, "session-created") == 0)
148                     control_notify_session_created(ne->session);
149           if (strcmp(ne->name, "session-closed") == 0)
150                     control_notify_session_closed(ne->session);
151           if (strcmp(ne->name, "session-window-changed") == 0)
152                     control_notify_session_window_changed(ne->session);
153           if (strcmp(ne->name, "paste-buffer-changed") == 0)
154                     control_notify_paste_buffer_changed(ne->pbname);
155           if (strcmp(ne->name, "paste-buffer-deleted") == 0)
156                     control_notify_paste_buffer_deleted(ne->pbname);
157 
158           notify_insert_hook(item, ne);
159 
160           if (ne->client != NULL)
161                     server_client_unref(ne->client);
162           if (ne->session != NULL)
163                     session_remove_ref(ne->session, __func__);
164           if (ne->window != NULL)
165                     window_remove_ref(ne->window, __func__);
166 
167           if (ne->fs.s != NULL)
168                     session_remove_ref(ne->fs.s, __func__);
169 
170           format_free(ne->formats);
171           free(__UNCONST(ne->name));
172           free(__UNCONST(ne->pbname));
173           free(ne);
174 
175           return (CMD_RETURN_NORMAL);
176 }
177 
178 static void
notify_add(const char * name,struct cmd_find_state * fs,struct client * c,struct session * s,struct window * w,struct window_pane * wp,const char * pbname)179 notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
180     struct session *s, struct window *w, struct window_pane *wp,
181     const char *pbname)
182 {
183           struct notify_entry *ne;
184           struct cmdq_item    *item;
185 
186           item = cmdq_running(NULL);
187           if (item != NULL && (cmdq_get_flags(item) & CMDQ_STATE_NOHOOKS))
188                     return;
189 
190           ne = xcalloc(1, sizeof *ne);
191           ne->name = xstrdup(name);
192 
193           ne->client = c;
194           ne->session = s;
195           ne->window = w;
196           ne->pane = (wp != NULL ? (int)wp->id : -1);
197           ne->pbname = (pbname != NULL ? xstrdup(pbname) : NULL);
198 
199           ne->formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
200           format_add(ne->formats, "hook", "%s", name);
201           if (c != NULL)
202                     format_add(ne->formats, "hook_client", "%s", c->name);
203           if (s != NULL) {
204                     format_add(ne->formats, "hook_session", "$%u", s->id);
205                     format_add(ne->formats, "hook_session_name", "%s", s->name);
206           }
207           if (w != NULL) {
208                     format_add(ne->formats, "hook_window", "@%u", w->id);
209                     format_add(ne->formats, "hook_window_name", "%s", w->name);
210           }
211           if (wp != NULL)
212                     format_add(ne->formats, "hook_pane", "%%%d", wp->id);
213           format_log_debug(ne->formats, __func__);
214 
215           if (c != NULL)
216                     c->references++;
217           if (s != NULL)
218                     session_add_ref(s, __func__);
219           if (w != NULL)
220                     window_add_ref(w, __func__);
221 
222           cmd_find_copy_state(&ne->fs, fs);
223           if (ne->fs.s != NULL) /* cmd_find_valid_state needs session */
224                     session_add_ref(ne->fs.s, __func__);
225 
226           cmdq_append(NULL, cmdq_get_callback(notify_callback, ne));
227 }
228 
229 void
notify_hook(struct cmdq_item * item,const char * name)230 notify_hook(struct cmdq_item *item, const char *name)
231 {
232           struct cmd_find_state         *target = cmdq_get_target(item);
233           struct notify_entry  ne;
234 
235           memset(&ne, 0, sizeof ne);
236 
237           ne.name = name;
238           cmd_find_copy_state(&ne.fs, target);
239 
240           ne.client = cmdq_get_client(item);
241           ne.session = target->s;
242           ne.window = target->w;
243           ne.pane = (target->wp != NULL ? (int)target->wp->id : -1);
244 
245           ne.formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
246           format_add(ne.formats, "hook", "%s", name);
247           format_log_debug(ne.formats, __func__);
248 
249           notify_insert_hook(item, &ne);
250           format_free(ne.formats);
251 }
252 
253 void
notify_client(const char * name,struct client * c)254 notify_client(const char *name, struct client *c)
255 {
256           struct cmd_find_state         fs;
257 
258           cmd_find_from_client(&fs, c, 0);
259           notify_add(name, &fs, c, NULL, NULL, NULL, NULL);
260 }
261 
262 void
notify_session(const char * name,struct session * s)263 notify_session(const char *name, struct session *s)
264 {
265           struct cmd_find_state         fs;
266 
267           if (session_alive(s))
268                     cmd_find_from_session(&fs, s, 0);
269           else
270                     cmd_find_from_nothing(&fs, 0);
271           notify_add(name, &fs, NULL, s, NULL, NULL, NULL);
272 }
273 
274 void
notify_winlink(const char * name,struct winlink * wl)275 notify_winlink(const char *name, struct winlink *wl)
276 {
277           struct cmd_find_state         fs;
278 
279           cmd_find_from_winlink(&fs, wl, 0);
280           notify_add(name, &fs, NULL, wl->session, wl->window, NULL, NULL);
281 }
282 
283 void
notify_session_window(const char * name,struct session * s,struct window * w)284 notify_session_window(const char *name, struct session *s, struct window *w)
285 {
286           struct cmd_find_state         fs;
287 
288           cmd_find_from_session_window(&fs, s, w, 0);
289           notify_add(name, &fs, NULL, s, w, NULL, NULL);
290 }
291 
292 void
notify_window(const char * name,struct window * w)293 notify_window(const char *name, struct window *w)
294 {
295           struct cmd_find_state         fs;
296 
297           cmd_find_from_window(&fs, w, 0);
298           notify_add(name, &fs, NULL, NULL, w, NULL, NULL);
299 }
300 
301 void
notify_pane(const char * name,struct window_pane * wp)302 notify_pane(const char *name, struct window_pane *wp)
303 {
304           struct cmd_find_state         fs;
305 
306           cmd_find_from_pane(&fs, wp, 0);
307           notify_add(name, &fs, NULL, NULL, NULL, wp, NULL);
308 }
309 
310 void
notify_paste_buffer(const char * pbname,int deleted)311 notify_paste_buffer(const char *pbname, int deleted)
312 {
313           struct cmd_find_state         fs;
314 
315           cmd_find_clear_state(&fs, 0);
316           if (deleted) {
317                     notify_add("paste-buffer-deleted", &fs, NULL, NULL, NULL, NULL,
318                         pbname);
319           } else {
320                     notify_add("paste-buffer-changed", &fs, NULL, NULL, NULL, NULL,
321                         pbname);
322           }
323 }
324