Merge Irssi 0.8.16-rc1
[silc.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 along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 "settings.h"
29 #include "irssi-version.h"
30 #include "servers.h"
31
32 #include "fe-windows.h"
33 #include "printtext.h"
34
35 #define PASTE_CHECK_SPEED 200 /* 0.2 sec */
36
37 static int ret_texts[] = {
38         TXT_OPTION_UNKNOWN,
39         TXT_OPTION_AMBIGUOUS,
40         TXT_OPTION_MISSING_ARG,
41         TXT_COMMAND_UNKNOWN,
42         TXT_COMMAND_AMBIGUOUS,
43         -1,
44         TXT_NOT_ENOUGH_PARAMS,
45         TXT_NOT_CONNECTED,
46         TXT_NOT_JOINED,
47         TXT_CHAN_NOT_FOUND,
48         TXT_CHAN_NOT_SYNCED,
49         TXT_ILLEGAL_PROTO,
50         TXT_NOT_GOOD_IDEA,
51         TXT_INVALID_TIME,
52         TXT_INVALID_CHARSET,
53         TXT_EVAL_MAX_RECURSE,
54         TXT_PROGRAM_NOT_FOUND
55 };
56
57 int command_hide_output;
58
59 /* keep the whole command line here temporarily. we need it in
60    "default command" event handler, but there we don't know if the start of
61    the line had one or two command chars, and which one.. */
62 static const char *current_cmdline;
63
64 static GTimeVal time_command_last, time_command_now;
65 static int last_command_cmd, command_cmd;
66
67 /* SYNTAX: ECHO [-current] [-window <name>] [-level <level>] <text> */
68 static void cmd_echo(const char *data, void *server, WI_ITEM_REC *item)
69 {
70         WINDOW_REC *window;
71         GHashTable *optlist;
72         char *msg, *levelstr, *winname;
73         void *free_arg;
74         int level;
75
76         g_return_if_fail(data != NULL);
77
78         if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
79                             PARAM_FLAG_GETREST, "echo", &optlist, &msg))
80                 return;
81
82         levelstr = g_hash_table_lookup(optlist, "level");
83         level = levelstr == NULL ? 0 :
84                 level2bits(g_hash_table_lookup(optlist, "level"), NULL);
85         if (level == 0) level = MSGLEVEL_CRAP;
86
87         winname = g_hash_table_lookup(optlist, "window");
88         window = winname == NULL ? NULL :
89                 is_numeric(winname, '\0') ?
90                 window_find_refnum(atoi(winname)) :
91                 window_find_item(NULL, winname);
92         if (window == NULL) window = active_win;
93
94         printtext_window(window, level, "%s", msg);
95         cmd_params_free(free_arg);
96 }
97
98 /* SYNTAX: VERSION */
99 static void cmd_version(char *data)
100 {
101         char time[10];
102
103         g_return_if_fail(data != NULL);
104
105         if (*data == '\0') {
106                 g_snprintf(time, sizeof(time), "%04d", IRSSI_VERSION_TIME);
107                 printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
108                           "Client: "PACKAGE" " IRSSI_VERSION" (%d %s)",
109                           IRSSI_VERSION_DATE, time);
110         }
111 }
112
113 /* SYNTAX: CAT <file> */
114 static void cmd_cat(const char *data)
115 {
116         char *fname, *fposstr;
117         void *free_arg;
118         int fpos;
119         GIOChannel *handle;
120         GString *buf;
121         gsize tpos;
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         handle = g_io_channel_new_file(fname, "r", NULL);
131         g_free(fname);
132
133         if (handle == NULL) {
134                 /* file not found */
135                 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
136                           "%s", g_strerror(errno));
137                 return;
138         }
139
140         g_io_channel_set_encoding(handle, NULL, NULL);
141         g_io_channel_seek_position(handle, fpos, G_SEEK_SET, NULL);
142         buf = g_string_sized_new(512);
143         while (g_io_channel_read_line_string(handle, buf, &tpos, NULL) == G_IO_STATUS_NORMAL) {
144                 buf->str[tpos] = '\0';
145                 printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP |
146                           MSGLEVEL_NEVER, "%s", buf->str);
147         }
148         g_string_free(buf, TRUE);
149
150         g_io_channel_unref(handle);
151 }
152
153 /* SYNTAX: BEEP */
154 static void cmd_beep(void)
155 {
156         signal_emit("beep", 0);
157 }
158
159 static void cmd_nick(const char *data, SERVER_REC *server)
160 {
161         g_return_if_fail(data != NULL);
162
163         if (*data != '\0') return;
164         if (server == NULL || !server->connected)
165                 cmd_return_error(CMDERR_NOT_CONNECTED);
166
167         /* display current nick */
168         printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_YOUR_NICK, server->nick);
169         signal_stop();
170 }
171
172 /* SYNTAX: UPTIME */
173 static void cmd_uptime(char *data)
174 {
175         long uptime;
176
177         g_return_if_fail(data != NULL);
178
179         if (*data == '\0') {
180                 uptime = time(NULL) - client_start_time;
181                 printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
182                           "Uptime: %ldd %ldh %ldm %lds",
183                           uptime/3600/24, uptime/3600%24,
184                           uptime/60%60, uptime%60);
185         }
186 }
187
188 static void sig_stop(void)
189 {
190         signal_stop();
191 }
192
193 static void event_command(const char *data)
194 {
195         const char *cmdchar;
196
197         /* save current command line */
198         current_cmdline = data;
199
200         /* for detecting if we're pasting text */
201         time_command_last = time_command_now;
202         last_command_cmd = command_cmd;
203
204         g_get_current_time(&time_command_now);
205         command_cmd = *data != '\0' &&
206                 strchr(settings_get_str("cmdchars"), *data) != NULL;
207
208         /* /^command hides the output of the command */
209         cmdchar = *data == '\0' ? NULL :
210                 strchr(settings_get_str("cmdchars"), *data);
211         if (cmdchar != NULL && (data[1] == '^' ||
212                                 (data[1] == *cmdchar && data[2] == '^'))
213                             && !command_hide_output++) {
214                 signal_add_first("print starting", (SIGNAL_FUNC) sig_stop);
215                 signal_add_first("print format", (SIGNAL_FUNC) sig_stop);
216                 signal_add_first("print text", (SIGNAL_FUNC) sig_stop);
217         }
218 }
219
220 static void event_command_last(const char *data)
221 {
222         if (command_hide_output && !--command_hide_output) {
223                 signal_remove("print starting", (SIGNAL_FUNC) sig_stop);
224                 signal_remove("print format", (SIGNAL_FUNC) sig_stop);
225                 signal_remove("print text", (SIGNAL_FUNC) sig_stop);
226         }
227 }
228
229 static void event_default_command(const char *data, void *server,
230                                   WI_ITEM_REC *item)
231 {
232         const char *cmdchars, *ptr;
233         char *cmd, *p;
234         long diff;
235
236         cmdchars = settings_get_str("cmdchars");
237
238         ptr = data;
239         while (*ptr != '\0' && *ptr != ' ') {
240                 if (strchr(cmdchars, *ptr)) {
241                         /* command character inside command .. we probably
242                            want to send this text to channel. for example
243                            when pasting a path /usr/bin/xxx. */
244                         signal_emit("send text", 3, current_cmdline, server, item);
245                         return;
246                 }
247                 ptr++;
248         }
249
250         /* maybe we're copy+pasting text? check how long it was since the
251            last line */
252         diff = get_timeval_diff(&time_command_now, &time_command_last);
253         if (item != NULL && !last_command_cmd && diff < PASTE_CHECK_SPEED) {
254                 signal_emit("send text", 3, current_cmdline, active_win->active_server, active_win->active);
255                 command_cmd = FALSE;
256                 return;
257         }
258
259         /* get the command part of the line, send "error command" signal */
260         cmd = g_strdup(data);
261         p = strchr(cmd, ' ');
262         if (p != NULL) *p = '\0';
263
264         signal_emit("error command", 2, GINT_TO_POINTER(CMDERR_UNKNOWN), cmd);
265
266         g_free(cmd);
267 }
268
269 static void event_cmderror(void *errorp, const char *arg)
270 {
271         int error;
272
273         error = GPOINTER_TO_INT(errorp);
274         if (error == CMDERR_ERRNO) {
275                 /* errno is special */
276                 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", g_strerror(errno));
277         } else {
278                 /* others */
279                 printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, ret_texts[error + -CMDERR_OPTION_UNKNOWN], arg);
280         }
281 }
282
283 static void event_list_subcommands(const char *command)
284 {
285         GSList *tmp;
286         GString *str;
287         int len;
288
289         str = g_string_new(NULL);
290
291         len = strlen(command);
292         for (tmp = commands; tmp != NULL; tmp = tmp->next) {
293                 COMMAND_REC *rec = tmp->data;
294
295                 if (g_strncasecmp(rec->cmd, command, len) == 0 &&
296                     rec->cmd[len] == ' ' &&
297                     strchr(rec->cmd+len+1, ' ') == NULL) {
298                         g_string_append_printf(str, "%s ", rec->cmd+len+1);
299                 }
300         }
301
302         if (str->len != 0) {
303                 g_string_truncate(str, str->len-1);
304                 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str->str);
305         }
306
307         g_string_free(str, TRUE);
308 }
309
310 void fe_core_commands_init(void)
311 {
312         command_hide_output = 0;
313
314         command_cmd = FALSE;
315         memset(&time_command_now, 0, sizeof(GTimeVal));
316
317         command_bind("echo", NULL, (SIGNAL_FUNC) cmd_echo);
318         command_bind("version", NULL, (SIGNAL_FUNC) cmd_version);
319         command_bind("cat", NULL, (SIGNAL_FUNC) cmd_cat);
320         command_bind("beep", NULL, (SIGNAL_FUNC) cmd_beep);
321         command_bind("uptime", NULL, (SIGNAL_FUNC) cmd_uptime);
322         command_bind_first("nick", NULL, (SIGNAL_FUNC) cmd_nick);
323
324         signal_add("send command", (SIGNAL_FUNC) event_command);
325         signal_add_last("send command", (SIGNAL_FUNC) event_command_last);
326         signal_add("default command", (SIGNAL_FUNC) event_default_command);
327         signal_add("error command", (SIGNAL_FUNC) event_cmderror);
328         signal_add("list subcommands", (SIGNAL_FUNC) event_list_subcommands);
329
330         command_set_options("echo", "current +level +window");
331 }
332
333 void fe_core_commands_deinit(void)
334 {
335         command_unbind("echo", (SIGNAL_FUNC) cmd_echo);
336         command_unbind("version", (SIGNAL_FUNC) cmd_version);
337         command_unbind("cat", (SIGNAL_FUNC) cmd_cat);
338         command_unbind("beep", (SIGNAL_FUNC) cmd_beep);
339         command_unbind("uptime", (SIGNAL_FUNC) cmd_uptime);
340         command_unbind("nick", (SIGNAL_FUNC) cmd_nick);
341
342         signal_remove("send command", (SIGNAL_FUNC) event_command);
343         signal_remove("send command", (SIGNAL_FUNC) event_command_last);
344         signal_remove("default command", (SIGNAL_FUNC) event_default_command);
345         signal_remove("error command", (SIGNAL_FUNC) event_cmderror);
346         signal_remove("list subcommands", (SIGNAL_FUNC) event_list_subcommands);
347 }