Merged 0.7.99 irssi.
[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, "%s", str);
141         } while (ret > 0);
142         line_split_free(buffer);
143
144         close(f);
145 }
146
147 /* SYNTAX: BEEP */
148 static void cmd_beep(void)
149 {
150         signal_emit("beep", 0);
151 }
152
153 static void cmd_nick(const char *data, SERVER_REC *server)
154 {
155         g_return_if_fail(data != NULL);
156
157         if (*data != '\0') return;
158         if (server == NULL || !server->connected)
159                 cmd_return_error(CMDERR_NOT_CONNECTED);
160
161         /* display current nick */
162         printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_YOUR_NICK, server->nick);
163         signal_stop();
164 }
165
166 static void cmd_join(const char *data, SERVER_REC *server)
167 {
168         GHashTable *optlist;
169         char *channels;
170         void *free_arg;
171
172         g_return_if_fail(data != NULL);
173
174         if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
175                             PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
176                             "join", &optlist, &channels))
177                 return;
178
179         server = cmd_options_get_server("join", optlist, server);
180         if (g_hash_table_lookup(optlist, "invite") &&
181             server != NULL && server->last_invite == NULL) {
182                 /* ..all this trouble just to print this error message */
183                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_NOT_INVITED);
184                 signal_stop();
185         }
186
187         cmd_params_free(free_arg);
188 }
189
190 static void sig_stop(void)
191 {
192         signal_stop();
193 }
194
195 static void event_command(const char *data)
196 {
197         const char *cmdchar;
198
199         if (*data == '\0') {
200                 /* empty line, forget it. */
201                 signal_stop();
202                 return;
203         }
204
205         /* save current command line */
206         current_cmdline = data;
207
208         /* for detecting if we're pasting text */
209         time_command_last = time_command_now;
210         last_command_cmd = command_cmd;
211
212         g_get_current_time(&time_command_now);
213         command_cmd = strchr(settings_get_str("cmdchars"), *data) != NULL;
214
215         /* /^command hides the output of the command */
216         cmdchar = strchr(settings_get_str("cmdchars"), *data);
217         if (cmdchar != NULL && (data[1] == '^' ||
218                                 (data[1] == *cmdchar && data[2] == '^'))) {
219                 hide_output = TRUE;
220                 signal_add_first("print starting", (SIGNAL_FUNC) sig_stop);
221                 signal_add_first("print format", (SIGNAL_FUNC) sig_stop);
222                 signal_add_first("print text", (SIGNAL_FUNC) sig_stop);
223         }
224 }
225
226 static void event_command_last(const char *data)
227 {
228         if (hide_output) {
229                 hide_output = FALSE;
230                 signal_remove("print starting", (SIGNAL_FUNC) sig_stop);
231                 signal_remove("print format", (SIGNAL_FUNC) sig_stop);
232                 signal_remove("print text", (SIGNAL_FUNC) sig_stop);
233         }
234 }
235
236 static void event_default_command(const char *data, void *server,
237                                   WI_ITEM_REC *item)
238 {
239         const char *cmdchars, *ptr;
240         char *cmd, *p;
241         long diff;
242
243         cmdchars = settings_get_str("cmdchars");
244
245         ptr = data;
246         while (*ptr != '\0' && *ptr != ' ') {
247                 if (strchr(cmdchars, *ptr)) {
248                         /* command character inside command .. we probably
249                            want to send this text to channel. for example
250                            when pasting a path /usr/bin/xxx. */
251                         signal_emit("send text", 3, current_cmdline, server, item);
252                         return;
253                 }
254                 ptr++;
255         }
256
257         /* maybe we're copy+pasting text? check how long it was since the
258            last line */
259         diff = get_timeval_diff(&time_command_now, &time_command_last);
260         if (item != NULL && !last_command_cmd && diff < PASTE_CHECK_SPEED) {
261                 signal_emit("send text", 3, current_cmdline, active_win->active_server, active_win->active);
262                 command_cmd = FALSE;
263                 return;
264         }
265
266         /* get the command part of the line, send "error command" signal */
267         cmd = g_strdup(data);
268         p = strchr(cmd, ' ');
269         if (p != NULL) *p = '\0';
270
271         signal_emit("error command", 2, GINT_TO_POINTER(CMDERR_UNKNOWN), cmd);
272
273         g_free(cmd);
274 }
275
276 static void event_cmderror(void *errorp, const char *arg)
277 {
278         int error;
279
280         error = GPOINTER_TO_INT(errorp);
281         if (error == CMDERR_ERRNO) {
282                 /* errno is special */
283                 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", g_strerror(errno));
284         } else {
285                 /* others */
286                 printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, ret_texts[error + -CMDERR_OPTION_UNKNOWN], arg);
287         }
288 }
289
290 static void event_list_subcommands(const char *command)
291 {
292         GSList *tmp;
293         GString *str;
294         int len;
295
296         str = g_string_new(NULL);
297
298         len = strlen(command);
299         for (tmp = commands; tmp != NULL; tmp = tmp->next) {
300                 COMMAND_REC *rec = tmp->data;
301
302                 if (g_strncasecmp(rec->cmd, command, len) == 0 &&
303                     rec->cmd[len] == ' ' &&
304                     strchr(rec->cmd+len+1, ' ') == NULL) {
305                         g_string_sprintfa(str, "%s ", rec->cmd+len+1);
306                 }
307         }
308
309         if (str->len != 0) {
310                 g_string_truncate(str, str->len-1);
311                 printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str->str);
312         }
313
314         g_string_free(str, TRUE);
315 }
316
317 void fe_core_commands_init(void)
318 {
319         hide_output = FALSE;
320
321         command_cmd = FALSE;
322         memset(&time_command_now, 0, sizeof(GTimeVal));
323
324         command_bind("echo", NULL, (SIGNAL_FUNC) cmd_echo);
325         command_bind("version", NULL, (SIGNAL_FUNC) cmd_version);
326         command_bind("cat", NULL, (SIGNAL_FUNC) cmd_cat);
327         command_bind("beep", NULL, (SIGNAL_FUNC) cmd_beep);
328         command_bind_first("nick", NULL, (SIGNAL_FUNC) cmd_nick);
329         command_bind_first("join", NULL, (SIGNAL_FUNC) cmd_join);
330
331         signal_add("send command", (SIGNAL_FUNC) event_command);
332         signal_add_last("send command", (SIGNAL_FUNC) event_command_last);
333         signal_add("default command", (SIGNAL_FUNC) event_default_command);
334         signal_add("error command", (SIGNAL_FUNC) event_cmderror);
335         signal_add("list subcommands", (SIGNAL_FUNC) event_list_subcommands);
336
337         command_set_options("echo", "current +level +window");
338 }
339
340 void fe_core_commands_deinit(void)
341 {
342         command_unbind("echo", (SIGNAL_FUNC) cmd_echo);
343         command_unbind("version", (SIGNAL_FUNC) cmd_version);
344         command_unbind("cat", (SIGNAL_FUNC) cmd_cat);
345         command_unbind("beep", (SIGNAL_FUNC) cmd_beep);
346         command_unbind("nick", (SIGNAL_FUNC) cmd_nick);
347         command_unbind("join", (SIGNAL_FUNC) cmd_join);
348
349         signal_remove("send command", (SIGNAL_FUNC) event_command);
350         signal_remove("send command", (SIGNAL_FUNC) event_command_last);
351         signal_remove("default command", (SIGNAL_FUNC) event_default_command);
352         signal_remove("error command", (SIGNAL_FUNC) event_cmderror);
353         signal_remove("list subcommands", (SIGNAL_FUNC) event_list_subcommands);
354 }