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