Added SILC Thread Queue API
[crypto.git] / apps / irssi / src / fe-common / core / fe-core-commands.c
1 /*
2  fe-core-commands.c : irssi
3
4     Copyright (C) 1999-2001 Timo Sirainen
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "core.h"
22 #include "module.h"
23 #include "module-formats.h"
24 #include "signals.h"
25 #include "commands.h"
26 #include "levels.h"
27 #include "misc.h"
28 #include "line-split.h"
29 #include "settings.h"
30 #include "irssi-version.h"
31 #include "servers.h"
32
33 #include "fe-windows.h"
34 #include "printtext.h"
35
36 #define PASTE_CHECK_SPEED 200 /* 0.2 sec */
37
38 static int ret_texts[] = {
39         TXT_OPTION_UNKNOWN,
40         TXT_OPTION_AMBIGUOUS,
41         TXT_OPTION_MISSING_ARG,
42         TXT_COMMAND_UNKNOWN,
43         TXT_COMMAND_AMBIGUOUS,
44         -1,
45         TXT_NOT_ENOUGH_PARAMS,
46         TXT_NOT_CONNECTED,
47         TXT_NOT_JOINED,
48         TXT_CHAN_NOT_FOUND,
49         TXT_CHAN_NOT_SYNCED,
50         TXT_ILLEGAL_PROTO,
51         TXT_NOT_GOOD_IDEA,
52         TXT_INVALID_TIME,
53         TXT_INVALID_CHARSET,
54         TXT_EVAL_MAX_RECURSE,
55         TXT_PROGRAM_NOT_FOUND
56 };
57
58 int command_hide_output;
59
60 /* keep the whole command line here temporarily. we need it in
61    "default command" event handler, but there we don't know if the start of
62    the line had one or two command chars, and which one.. */
63 static const char *current_cmdline;
64
65 static GTimeVal time_command_last, time_command_now;
66 static int last_command_cmd, command_cmd;
67
68 /* SYNTAX: ECHO [-current] [-window <name>] [-level <level>] <text> */
69 static void cmd_echo(const char *data, void *server, WI_ITEM_REC *item)
70 {
71         WINDOW_REC *window;
72         GHashTable *optlist;
73         char *msg, *levelstr, *winname;
74         void *free_arg;
75         int level;
76
77         g_return_if_fail(data != NULL);
78
79         if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
80                             PARAM_FLAG_GETREST, "echo", &optlist, &msg))
81                 return;
82
83         levelstr = g_hash_table_lookup(optlist, "level");
84         level = levelstr == NULL ? 0 :
85                 level2bits(g_hash_table_lookup(optlist, "level"));
86         if (level == 0) level = MSGLEVEL_CRAP;
87
88         winname = g_hash_table_lookup(optlist, "window");
89         window = winname == NULL ? NULL :
90                 is_numeric(winname, '\0') ?
91                 window_find_refnum(atoi(winname)) :
92                 window_find_item(NULL, winname);
93         if (window == NULL) window = active_win;
94
95         printtext_window(window, level, "%s", msg);
96         cmd_params_free(free_arg);
97 }
98
99 /* SYNTAX: VERSION */
100 static void cmd_version(char *data)
101 {
102         char time[10];
103
104         g_return_if_fail(data != NULL);
105
106         if (*data == '\0') {
107                 g_snprintf(time, sizeof(time), "%04d", IRSSI_VERSION_TIME);
108                 printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
109                           "Client: "PACKAGE" " IRSSI_VERSION" (%d %s)",
110                           IRSSI_VERSION_DATE, time);
111         }
112 }
113
114 /* SYNTAX: CAT <file> */
115 static void cmd_cat(const char *data)
116 {
117         LINEBUF_REC *buffer = NULL;
118         char *fname, *fposstr;
119         char tmpbuf[1024], *str;
120         void *free_arg;
121         int f, ret, recvlen, fpos;
122
123         if (!cmd_get_params(data, &free_arg, 2, &fname, &fposstr))
124                 return;
125
126         fname = convert_home(fname);
127         fpos = atoi(fposstr);
128         cmd_params_free(free_arg);
129
130         f = open(fname, O_RDONLY);
131         g_free(fname);
132
133         if (f == -1) {
134                 /* file not found */
135                 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
136                           "%s", g_strerror(errno));
137                 return;
138         }
139
140         lseek(f, fpos, SEEK_SET);
141         do {
142                 recvlen = read(f, tmpbuf, sizeof(tmpbuf));
143
144                 ret = line_split(tmpbuf, recvlen, &str, &buffer);
145                 if (ret > 0) {
146                         printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP |
147                                   MSGLEVEL_NEVER, "%s", str);
148                 }
149         } while (ret > 0);
150         line_split_free(buffer);
151
152         close(f);
153 }
154
155 /* SYNTAX: BEEP */
156 static void cmd_beep(void)
157 {
158         signal_emit("beep", 0);
159 }
160
161 static void cmd_nick(const char *data, SERVER_REC *server)
162 {
163         g_return_if_fail(data != NULL);
164
165         if (*data != '\0') return;
166         if (server == NULL || !server->connected)
167                 cmd_return_error(CMDERR_NOT_CONNECTED);
168
169         /* display current nick */
170         printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_YOUR_NICK, server->nick);
171         signal_stop();
172 }
173
174 static void cmd_join(const char *data, SERVER_REC *server)
175 {
176         GHashTable *optlist;
177         char *channels;
178         void *free_arg;
179
180         g_return_if_fail(data != NULL);
181
182         if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
183                             PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
184                             "join", &optlist, &channels))
185                 return;
186
187         server = cmd_options_get_server("join", optlist, server);
188         if (g_hash_table_lookup(optlist, "invite") &&
189             server != NULL && server->last_invite == NULL) {
190                 /* ..all this trouble just to print this error message */
191                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_NOT_INVITED);
192                 signal_stop();
193         }
194
195         cmd_params_free(free_arg);
196 }
197
198 /* SYNTAX: UPTIME */
199 static void cmd_uptime(char *data)
200 {
201         time_t uptime;
202
203         g_return_if_fail(data != NULL);
204
205         if (*data == '\0') {
206                 uptime = time(NULL) - client_start_time;
207                 printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
208                           "Uptime: %ldd %ldh %ldm %lds",
209                           uptime/3600/24, uptime/3600%24,
210                           uptime/60%60, uptime%60);
211         }
212 }
213
214 static void sig_stop(void)
215 {
216         signal_stop();
217 }
218
219 static void event_command(const char *data)
220 {
221         const char *cmdchar;
222
223         /* save current command line */
224         current_cmdline = data;
225
226         /* for detecting if we're pasting text */
227         time_command_last = time_command_now;
228         last_command_cmd = command_cmd;
229
230         g_get_current_time(&time_command_now);
231         command_cmd = *data != '\0' &&
232                 strchr(settings_get_str("cmdchars"), *data) != NULL;
233
234         /* /^command hides the output of the command */
235         cmdchar = *data == '\0' ? NULL :
236                 strchr(settings_get_str("cmdchars"), *data);
237         if (cmdchar != NULL && (data[1] == '^' ||
238                                 (data[1] == *cmdchar && data[2] == '^'))
239                             && !command_hide_output++) {
240                 signal_add_first("print starting", (SIGNAL_FUNC) sig_stop);
241                 signal_add_first("print format", (SIGNAL_FUNC) sig_stop);
242                 signal_add_first("print text", (SIGNAL_FUNC) sig_stop);
243         }
244 }
245
246 static void event_command_last(const char *data)
247 {
248         if (command_hide_output && !--command_hide_output) {
249                 signal_remove("print starting", (SIGNAL_FUNC) sig_stop);
250                 signal_remove("print format", (SIGNAL_FUNC) sig_stop);
251                 signal_remove("print text", (SIGNAL_FUNC) sig_stop);
252         }
253 }
254
255 static void event_default_command(const char *data, void *server,
256                                   WI_ITEM_REC *item)
257 {
258         const char *cmdchars, *ptr;
259         char *cmd, *p;
260         long diff;
261
262         cmdchars = settings_get_str("cmdchars");
263
264         ptr = data;
265         while (*ptr != '\0' && *ptr != ' ') {
266                 if (strchr(cmdchars, *ptr)) {
267                         /* command character inside command .. we probably
268                            want to send this text to channel. for example
269                            when pasting a path /usr/bin/xxx. */
270                         signal_emit("send text", 3, current_cmdline, server, item);
271                         return;
272                 }
273                 ptr++;
274         }
275
276         /* maybe we're copy+pasting text? check how long it was since the
277            last line */
278         diff = get_timeval_diff(&time_command_now, &time_command_last);
279         if (item != NULL && !last_command_cmd && diff < PASTE_CHECK_SPEED) {
280                 signal_emit("send text", 3, current_cmdline, active_win->active_server, active_win->active);
281                 command_cmd = FALSE;
282                 return;
283         }
284
285         /* get the command part of the line, send "error command" signal */
286         cmd = g_strdup(data);
287         p = strchr(cmd, ' ');
288         if (p != NULL) *p = '\0';
289
290         signal_emit("error command", 2, GINT_TO_POINTER(CMDERR_UNKNOWN), cmd);
291
292         g_free(cmd);
293 }
294
295 static void event_cmderror(void *errorp, const char *arg)
296 {
297         int error;
298
299         error = GPOINTER_TO_INT(errorp);
300         if (error == CMDERR_ERRNO) {
301                 /* errno is special */
302                 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", g_strerror(errno));
303         } else {
304                 /* others */
305                 printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, ret_texts[error + -CMDERR_OPTION_UNKNOWN], arg);
306         }
307 }
308
309 static void event_list_subcommands(const char *command)
310 {
311         GSList *tmp;
312         GString *str;
313         int len;
314
315         str = g_string_new(NULL);
316
317         len = strlen(command);
318         for (tmp = commands; tmp != NULL; tmp = tmp->next) {
319                 COMMAND_REC *rec = tmp->data;
320
321                 if (g_strncasecmp(rec->cmd, command, len) == 0 &&
322                     rec->cmd[len] == ' ' &&
323                     strchr(rec->cmd+len+1, ' ') == NULL) {
324                         g_string_sprintfa(str, "%s ", rec->cmd+len+1);
325                 }
326         }
327
328         if (str->len != 0) {
329                 g_string_truncate(str, str->len-1);
330                 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str->str);
331         }
332
333         g_string_free(str, TRUE);
334 }
335
336 void fe_core_commands_init(void)
337 {
338         command_hide_output = 0;
339
340         command_cmd = FALSE;
341         memset(&time_command_now, 0, sizeof(GTimeVal));
342
343         command_bind("echo", NULL, (SIGNAL_FUNC) cmd_echo);
344         command_bind("version", NULL, (SIGNAL_FUNC) cmd_version);
345         command_bind("cat", NULL, (SIGNAL_FUNC) cmd_cat);
346         command_bind("beep", NULL, (SIGNAL_FUNC) cmd_beep);
347         command_bind("uptime", NULL, (SIGNAL_FUNC) cmd_uptime);
348         command_bind_first("nick", NULL, (SIGNAL_FUNC) cmd_nick);
349         command_bind_first("join", NULL, (SIGNAL_FUNC) cmd_join);
350
351         signal_add("send command", (SIGNAL_FUNC) event_command);
352         signal_add_last("send command", (SIGNAL_FUNC) event_command_last);
353         signal_add("default command", (SIGNAL_FUNC) event_default_command);
354         signal_add("error command", (SIGNAL_FUNC) event_cmderror);
355         signal_add("list subcommands", (SIGNAL_FUNC) event_list_subcommands);
356
357         command_set_options("echo", "current +level +window");
358 }
359
360 void fe_core_commands_deinit(void)
361 {
362         command_unbind("echo", (SIGNAL_FUNC) cmd_echo);
363         command_unbind("version", (SIGNAL_FUNC) cmd_version);
364         command_unbind("cat", (SIGNAL_FUNC) cmd_cat);
365         command_unbind("beep", (SIGNAL_FUNC) cmd_beep);
366         command_unbind("uptime", (SIGNAL_FUNC) cmd_uptime);
367         command_unbind("nick", (SIGNAL_FUNC) cmd_nick);
368         command_unbind("join", (SIGNAL_FUNC) cmd_join);
369
370         signal_remove("send command", (SIGNAL_FUNC) event_command);
371         signal_remove("send command", (SIGNAL_FUNC) event_command_last);
372         signal_remove("default command", (SIGNAL_FUNC) event_default_command);
373         signal_remove("error command", (SIGNAL_FUNC) event_cmderror);
374         signal_remove("list subcommands", (SIGNAL_FUNC) event_list_subcommands);
375 }