b599c8109cbbb79d2c25367f41bd8375957356a5
[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
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 "module.h"
22 #include "module-formats.h"
23 #include "signals.h"
24 #include "commands.h"
25 #include "levels.h"
26 #include "misc.h"
27 #include "line-split.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 };
52
53 int command_hide_output;
54
55 /* keep the whole command line here temporarily. we need it in
56    "default command" event handler, but there we don't know if the start of
57    the line had one or two command chars, and which one.. */
58 static const char *current_cmdline;
59
60 static GTimeVal time_command_last, time_command_now;
61 static int last_command_cmd, command_cmd;
62
63 /* SYNTAX: ECHO [-current] [-window <name>] [-level <level>] <text> */
64 static void cmd_echo(const char *data, void *server, WI_ITEM_REC *item)
65 {
66         WINDOW_REC *window;
67         GHashTable *optlist;
68         char *msg, *levelstr, *winname;
69         void *free_arg;
70         int level;
71
72         g_return_if_fail(data != NULL);
73
74         if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
75                             PARAM_FLAG_GETREST, "echo", &optlist, &msg))
76                 return;
77
78         levelstr = g_hash_table_lookup(optlist, "level");
79         level = levelstr == NULL ? 0 :
80                 level2bits(g_hash_table_lookup(optlist, "level"));
81         if (level == 0) level = MSGLEVEL_CRAP;
82
83         winname = g_hash_table_lookup(optlist, "window");
84         window = winname == NULL ? NULL :
85                 is_numeric(winname, '\0') ?
86                 window_find_refnum(atoi(winname)) :
87                 window_find_item(NULL, winname);
88         if (window == NULL) window = active_win;
89
90         printtext_window(window, level, "%s", msg);
91         cmd_params_free(free_arg);
92 }
93
94 /* SYNTAX: VERSION */
95 static void cmd_version(char *data)
96 {
97         char time[10];
98
99         g_return_if_fail(data != NULL);
100
101         if (*data == '\0') {
102                 g_snprintf(time, sizeof(time), "%04d", IRSSI_VERSION_TIME);
103                 printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
104                           "Client: "PACKAGE" " IRSSI_VERSION" (%d %s)",
105                           IRSSI_VERSION_DATE, time);
106         }
107 }
108
109 /* SYNTAX: CAT <file> */
110 static void cmd_cat(const char *data)
111 {
112         LINEBUF_REC *buffer = NULL;
113         char *fname, *fposstr;
114         char tmpbuf[1024], *str;
115         void *free_arg;
116         int f, ret, recvlen, fpos;
117
118         if (!cmd_get_params(data, &free_arg, 2, &fname, &fposstr))
119                 return;
120
121         fname = convert_home(fname);
122         fpos = atoi(fposstr);
123         cmd_params_free(free_arg);
124
125         f = open(fname, O_RDONLY);
126         g_free(fname);
127
128         if (f == -1) {
129                 /* file not found */
130                 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
131                           "%s", g_strerror(errno));
132                 return;
133         }
134
135         lseek(f, fpos, SEEK_SET);
136         do {
137                 recvlen = read(f, tmpbuf, sizeof(tmpbuf));
138
139                 ret = line_split(tmpbuf, recvlen, &str, &buffer);
140                 if (ret > 0) {
141                         printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP |
142                                   MSGLEVEL_NEVER, "%s", str);
143                 }
144         } while (ret > 0);
145         line_split_free(buffer);
146
147         close(f);
148 }
149
150 /* SYNTAX: BEEP */
151 static void cmd_beep(void)
152 {
153         signal_emit("beep", 0);
154 }
155
156 static void cmd_nick(const char *data, SERVER_REC *server)
157 {
158         g_return_if_fail(data != NULL);
159
160         if (*data != '\0') return;
161         if (server == NULL || !server->connected)
162                 cmd_return_error(CMDERR_NOT_CONNECTED);
163
164         /* display current nick */
165         printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_YOUR_NICK, server->nick);
166         signal_stop();
167 }
168
169 static void cmd_join(const char *data, SERVER_REC *server)
170 {
171         GHashTable *optlist;
172         char *channels;
173         void *free_arg;
174
175         g_return_if_fail(data != NULL);
176
177         if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
178                             PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
179                             "join", &optlist, &channels))
180                 return;
181
182         server = cmd_options_get_server("join", optlist, server);
183         if (g_hash_table_lookup(optlist, "invite") &&
184             server != NULL && server->last_invite == NULL) {
185                 /* ..all this trouble just to print this error message */
186                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_NOT_INVITED);
187                 signal_stop();
188         }
189
190         cmd_params_free(free_arg);
191 }
192
193 static void sig_stop(void)
194 {
195         signal_stop();
196 }
197
198 static void event_command(const char *data)
199 {
200         const char *cmdchar;
201
202         /* save current command line */
203         current_cmdline = data;
204
205         /* for detecting if we're pasting text */
206         time_command_last = time_command_now;
207         last_command_cmd = command_cmd;
208
209         g_get_current_time(&time_command_now);
210         command_cmd = *data != '\0' &&
211                 strchr(settings_get_str("cmdchars"), *data) != NULL;
212
213         /* /^command hides the output of the command */
214         cmdchar = *data == '\0' ? NULL :
215                 strchr(settings_get_str("cmdchars"), *data);
216         if (cmdchar != NULL && (data[1] == '^' ||
217                                 (data[1] == *cmdchar && data[2] == '^'))) {
218                 command_hide_output = TRUE;
219                 signal_add_first("print starting", (SIGNAL_FUNC) sig_stop);
220                 signal_add_first("print format", (SIGNAL_FUNC) sig_stop);
221                 signal_add_first("print text", (SIGNAL_FUNC) sig_stop);
222         }
223 }
224
225 static void event_command_last(const char *data)
226 {
227         if (command_hide_output) {
228                 command_hide_output = FALSE;
229                 signal_remove("print starting", (SIGNAL_FUNC) sig_stop);
230                 signal_remove("print format", (SIGNAL_FUNC) sig_stop);
231                 signal_remove("print text", (SIGNAL_FUNC) sig_stop);
232         }
233 }
234
235 static void event_default_command(const char *data, void *server,
236                                   WI_ITEM_REC *item)
237 {
238         const char *cmdchars, *ptr;
239         char *cmd, *p;
240         long diff;
241
242         cmdchars = settings_get_str("cmdchars");
243
244         ptr = data;
245         while (*ptr != '\0' && *ptr != ' ') {
246                 if (strchr(cmdchars, *ptr)) {
247                         /* command character inside command .. we probably
248                            want to send this text to channel. for example
249                            when pasting a path /usr/bin/xxx. */
250                         signal_emit("send text", 3, current_cmdline, server, item);
251                         return;
252                 }
253                 ptr++;
254         }
255
256         /* maybe we're copy+pasting text? check how long it was since the
257            last line */
258         diff = get_timeval_diff(&time_command_now, &time_command_last);
259         if (item != NULL && !last_command_cmd && diff < PASTE_CHECK_SPEED) {
260                 signal_emit("send text", 3, current_cmdline, active_win->active_server, active_win->active);
261                 command_cmd = FALSE;
262                 return;
263         }
264
265         /* get the command part of the line, send "error command" signal */
266         cmd = g_strdup(data);
267         p = strchr(cmd, ' ');
268         if (p != NULL) *p = '\0';
269
270         signal_emit("error command", 2, GINT_TO_POINTER(CMDERR_UNKNOWN), cmd);
271
272         g_free(cmd);
273 }
274
275 static void event_cmderror(void *errorp, const char *arg)
276 {
277         int error;
278
279         error = GPOINTER_TO_INT(errorp);
280         if (error == CMDERR_ERRNO) {
281                 /* errno is special */
282                 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", g_strerror(errno));
283         } else {
284                 /* others */
285                 printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, ret_texts[error + -CMDERR_OPTION_UNKNOWN], arg);
286         }
287 }
288
289 static void event_list_subcommands(const char *command)
290 {
291         GSList *tmp;
292         GString *str;
293         int len;
294
295         str = g_string_new(NULL);
296
297         len = strlen(command);
298         for (tmp = commands; tmp != NULL; tmp = tmp->next) {
299                 COMMAND_REC *rec = tmp->data;
300
301                 if (g_strncasecmp(rec->cmd, command, len) == 0 &&
302                     rec->cmd[len] == ' ' &&
303                     strchr(rec->cmd+len+1, ' ') == NULL) {
304                         g_string_sprintfa(str, "%s ", rec->cmd+len+1);
305                 }
306         }
307
308         if (str->len != 0) {
309                 g_string_truncate(str, str->len-1);
310                 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str->str);
311         }
312
313         g_string_free(str, TRUE);
314 }
315
316 void fe_core_commands_init(void)
317 {
318         command_hide_output = FALSE;
319
320         command_cmd = FALSE;
321         memset(&time_command_now, 0, sizeof(GTimeVal));
322
323         command_bind("echo", NULL, (SIGNAL_FUNC) cmd_echo);
324         command_bind("version", NULL, (SIGNAL_FUNC) cmd_version);
325         command_bind("cat", NULL, (SIGNAL_FUNC) cmd_cat);
326         command_bind("beep", NULL, (SIGNAL_FUNC) cmd_beep);
327         command_bind_first("nick", NULL, (SIGNAL_FUNC) cmd_nick);
328         command_bind_first("join", NULL, (SIGNAL_FUNC) cmd_join);
329
330         signal_add("send command", (SIGNAL_FUNC) event_command);
331         signal_add_last("send command", (SIGNAL_FUNC) event_command_last);
332         signal_add("default command", (SIGNAL_FUNC) event_default_command);
333         signal_add("error command", (SIGNAL_FUNC) event_cmderror);
334         signal_add("list subcommands", (SIGNAL_FUNC) event_list_subcommands);
335
336         command_set_options("echo", "current +level +window");
337 }
338
339 void fe_core_commands_deinit(void)
340 {
341         command_unbind("echo", (SIGNAL_FUNC) cmd_echo);
342         command_unbind("version", (SIGNAL_FUNC) cmd_version);
343         command_unbind("cat", (SIGNAL_FUNC) cmd_cat);
344         command_unbind("beep", (SIGNAL_FUNC) cmd_beep);
345         command_unbind("nick", (SIGNAL_FUNC) cmd_nick);
346         command_unbind("join", (SIGNAL_FUNC) cmd_join);
347
348         signal_remove("send command", (SIGNAL_FUNC) event_command);
349         signal_remove("send command", (SIGNAL_FUNC) event_command_last);
350         signal_remove("default command", (SIGNAL_FUNC) event_default_command);
351         signal_remove("error command", (SIGNAL_FUNC) event_cmderror);
352         signal_remove("list subcommands", (SIGNAL_FUNC) event_list_subcommands);
353 }