merges from irssi.org cvs.
[silc.git] / apps / irssi / src / fe-common / core / fe-help.c
1 /*
2  fe-help.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 "signals.h"
23 #include "commands.h"
24 #include "levels.h"
25 #include "misc.h"
26 #include "line-split.h"
27 #include "settings.h"
28
29 #include "printtext.h"
30 #include "formats.h"
31
32 static int commands_equal(COMMAND_REC *rec, COMMAND_REC *rec2)
33 {
34         int i;
35
36         if (rec->category == NULL && rec2->category != NULL)
37                 return -1;
38         if (rec2->category == NULL && rec->category != NULL)
39                 return 1;
40         if (rec->category != NULL && rec2->category != NULL) {
41                 i = strcmp(rec->category, rec2->category);
42                 if (i != 0)
43                         return i;
44         }
45
46         return strcmp(rec->cmd, rec2->cmd);
47 }
48
49 static int get_cmd_length(void *data)
50 {
51         return strlen(((COMMAND_REC *) data)->cmd);
52 }
53
54 static void help_category(GSList *cmdlist, int items)
55 {
56         WINDOW_REC *window;
57         TEXT_DEST_REC dest;
58         GString *str;
59         GSList *tmp;
60         int *columns, cols, rows, col, row, last_col_rows, max_width;
61         char *linebuf, *format, *stripped;
62
63         window = window_find_closest(NULL, NULL, MSGLEVEL_CLIENTCRAP);
64         max_width = window->width;
65
66         /* remove width of timestamp from max_width */
67         format_create_dest(&dest, NULL, NULL, MSGLEVEL_CLIENTCRAP, NULL);
68         format = format_get_line_start(current_theme, &dest, time(NULL));
69         if (format != NULL) {
70                 stripped = strip_codes(format);
71                 max_width -= strlen(stripped);
72                 g_free(stripped);
73                 g_free(format);
74         }
75
76         /* calculate columns */
77         cols = get_max_column_count(cmdlist, get_cmd_length,
78                                     max_width, 6, 1, 3, &columns, &rows);
79         cmdlist = columns_sort_list(cmdlist, rows);
80
81         /* rows in last column */
82         last_col_rows = rows-(cols*rows-g_slist_length(cmdlist));
83         if (last_col_rows == 0)
84                 last_col_rows = rows;
85
86         str = g_string_new(NULL);
87         linebuf = g_malloc(max_width+1);
88
89         col = 0; row = 0;
90         for (tmp = cmdlist; tmp != NULL; tmp = tmp->next) {
91                 COMMAND_REC *rec = tmp->data;
92
93                 memset(linebuf, ' ', columns[col]);
94                 linebuf[columns[col]] = '\0';
95                 memcpy(linebuf, rec->cmd, strlen(rec->cmd));
96                 g_string_append(str, linebuf);
97
98                 if (++col == cols) {
99                         printtext(NULL, NULL,
100                                   MSGLEVEL_CLIENTCRAP, "%s", str->str);
101                         g_string_truncate(str, 0);
102                         col = 0; row++;
103
104                         if (row == last_col_rows)
105                                 cols--;
106                 }
107         }
108         if (str->len != 0)
109                 printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%s", str->str);
110
111         g_slist_free(cmdlist);
112         g_string_free(str, TRUE);
113         g_free(columns);
114         g_free(linebuf);
115 }
116
117 static int show_help_file(const char *file)
118 {
119         const char *helppath;
120         char tmpbuf[1024], *str, *path;
121         LINEBUF_REC *buffer = NULL;
122         int f, ret, recvlen;
123
124         helppath = settings_get_str("help_path");
125
126         /* helpdir/command or helpdir/category/command */
127         path = g_strdup_printf("%s/%s", helppath, file);
128         f = open(path, O_RDONLY);
129         g_free(path);
130
131         if (f == -1)
132                 return FALSE;
133
134         /* just print to screen whatever is in the file */
135         do {
136                 recvlen = read(f, tmpbuf, sizeof(tmpbuf));
137
138                 ret = line_split(tmpbuf, recvlen, &str, &buffer);
139                 if (ret > 0) {
140                         str = g_strconcat("%|", str, NULL);
141                         printtext_string(NULL, NULL, MSGLEVEL_CLIENTCRAP, str);
142                         g_free(str);
143                 }
144         }
145         while (ret > 0);
146         line_split_free(buffer);
147
148         close(f);
149         return TRUE;
150 }
151
152 static void show_help(const char *data)
153 {
154         COMMAND_REC *rec, *last;
155         GSList *tmp, *cmdlist;
156         int items, findlen;
157         int header, found, fullmatch;
158
159         g_return_if_fail(data != NULL);
160
161         /* sort the commands list */
162         commands = g_slist_sort(commands, (GCompareFunc) commands_equal);
163
164         /* print command, sort by category */
165         cmdlist = NULL; last = NULL; header = FALSE; fullmatch = FALSE;
166         items = 0; findlen = strlen(data); found = FALSE;
167         for (tmp = commands; tmp != NULL; last = rec, tmp = tmp->next) {
168                 rec = tmp->data;
169
170                 if (last != NULL && rec->category != NULL &&
171                     (last->category == NULL ||
172                      strcmp(rec->category, last->category) != 0)) {
173                         /* category changed */
174                         if (items > 0) {
175                                 if (!header) {
176                                         printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "Irssi commands:");
177                                         header = TRUE;
178                                 }
179                                 if (last->category != NULL) {
180                                         printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "");
181                                         printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%s:", last->category);
182                                 }
183                                 help_category(cmdlist, items);
184                         }
185
186                         g_slist_free(cmdlist); cmdlist = NULL;
187                         items = 0;
188                 }
189
190                 if (last != NULL && g_strcasecmp(rec->cmd, last->cmd) == 0)
191                         continue; /* don't display same command twice */
192
193                 if ((int)strlen(rec->cmd) >= findlen &&
194                     g_strncasecmp(rec->cmd, data, findlen) == 0) {
195                         if (rec->cmd[findlen] == '\0') {
196                                 fullmatch = TRUE;
197                                 found = TRUE;
198                                 break;
199                         }
200                         else if (strchr(rec->cmd+findlen+1, ' ') == NULL) {
201                                 /* not a subcommand (and matches the query) */
202                                 items++;
203                                 cmdlist = g_slist_append(cmdlist, rec);
204                                 found = TRUE;
205                         }
206                 }
207         }
208
209         if ((!found || fullmatch) && !show_help_file(data)) {
210                 printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP,
211                           "No help for %s", data);
212         }
213
214         if (*data != '\0' && data[strlen(data)-1] != ' ' &&
215             command_have_sub(data)) {
216                 char *cmd;
217
218                 cmd = g_strconcat(data, " ", NULL);
219                 show_help(cmd);
220                 g_free(cmd);
221         }
222
223         if (items != 0) {
224                 /* display the last category */
225                 if (!header) {
226                         printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP,
227                                   "Irssi commands:");
228                         header = TRUE;
229                 }
230
231                 if (last->category != NULL) {
232                         printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "");
233                         printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP,
234                                   "%s:", last->category);
235                 }
236                 help_category(cmdlist, items);
237                 g_slist_free(cmdlist);
238         }
239 }
240
241 /* SYNTAX: HELP [<command>] */
242 static void cmd_help(const char *data)
243 {
244         char *cmd, *ptr;
245
246         cmd = g_strdup(data);
247         ptr = cmd+strlen(cmd);
248         while (ptr[-1] == ' ') ptr--; *ptr = '\0';
249
250         show_help(cmd);
251         g_free(cmd);
252 }
253
254 void fe_help_init(void)
255 {
256         settings_add_str("misc", "help_path", HELPDIR);
257         command_bind("help", NULL, (SIGNAL_FUNC) cmd_help);
258 }
259
260 void fe_help_deinit(void)
261 {
262         command_unbind("help", (SIGNAL_FUNC) cmd_help);
263 }