Added SILC Thread Queue API
[crypto.git] / apps / irssi / src / fe-common / core / command-history.c
1 /*
2  command-history.c : irssi
3
4     Copyright (C) 1999 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 "misc.h"
24 #include "special-vars.h"
25 #include "settings.h"
26
27 #include "fe-windows.h"
28 #include "window-items.h"
29
30 #include "command-history.h"
31
32 /* command history */
33 static HISTORY_REC *global_history;
34 static int window_history;
35 static GSList *histories;
36
37 void command_history_add(HISTORY_REC *history, const char *text)
38 {
39         GList *link;
40
41         g_return_if_fail(history != NULL);
42         g_return_if_fail(text != NULL);
43
44         link = g_list_last(history->list);
45         if (link != NULL && strcmp(link->data, text) == 0)
46           return; /* same as previous entry */
47
48         if (settings_get_int("max_command_history") < 1 || 
49             history->lines < settings_get_int("max_command_history"))
50                 history->lines++;
51         else {
52                 link = history->list;
53                 g_free(link->data);
54                 history->list = g_list_remove_link(history->list, link);
55                 g_list_free_1(link);
56         }
57
58         history->list = g_list_append(history->list, g_strdup(text));
59 }
60
61 HISTORY_REC *command_history_find(HISTORY_REC *history)
62 {
63         GSList *tmp;
64         tmp = g_slist_find(histories, history);
65
66         if (tmp == NULL)
67                 return NULL;
68         else
69                 return tmp->data;
70 }
71
72 HISTORY_REC *command_history_find_name(const char *name)
73 {
74         GSList *tmp;
75
76         if (name == NULL)
77                 return NULL;
78
79         for (tmp = histories; tmp != NULL; tmp = tmp->next) {
80                 HISTORY_REC *rec = tmp->data;
81                 
82                 if (rec->name != NULL && g_strcasecmp(rec->name, name) == 0)
83                         return rec;
84         }
85         
86         return NULL;
87 }
88
89 HISTORY_REC *command_history_current(WINDOW_REC *window)
90 {
91         HISTORY_REC *rec;
92
93         if (window == NULL)
94                 return global_history;
95
96         if (window_history)
97                 return window->history;
98
99         rec = command_history_find_name(window->history_name);
100         if (rec != NULL)
101                 return rec;
102
103         return global_history;
104 }
105
106 const char *command_history_prev(WINDOW_REC *window, const char *text)
107 {
108         HISTORY_REC *history;
109         GList *pos;
110
111         history = command_history_current(window);
112         pos = history->pos;
113
114         if (pos != NULL) {
115                 history->pos = history->pos->prev;
116                 if (history->pos == NULL)
117                         history->over_counter++;
118         } else {
119                 history->pos = g_list_last(history->list);
120         }
121
122         if (*text != '\0' &&
123             (pos == NULL || strcmp(pos->data, text) != 0)) {
124                 /* save the old entry to history */
125                 command_history_add(history, text);
126         }
127
128         return history->pos == NULL ? "" : history->pos->data;
129 }
130
131 const char *command_history_next(WINDOW_REC *window, const char *text)
132 {
133         HISTORY_REC *history;
134         GList *pos;
135
136         history = command_history_current(window);
137         pos = history->pos; 
138
139         if (pos != NULL)
140                 history->pos = history->pos->next;
141         else if (history->over_counter > 0) {
142                 history->over_counter--;
143                 history->pos = history->list;
144         }
145
146         if (*text != '\0' &&
147             (pos == NULL || strcmp(pos->data, text) != 0)) {
148                 /* save the old entry to history */
149                 command_history_add(history, text);
150         }
151         return history->pos == NULL ? "" : history->pos->data;
152 }
153
154 void command_history_clear_pos_func(HISTORY_REC *history, gpointer user_data)
155 {
156         history->over_counter = 0;
157         history->pos = NULL;
158 }
159
160 void command_history_clear_pos(WINDOW_REC *window)
161 {
162         g_slist_foreach(histories, 
163                        (GFunc) command_history_clear_pos_func, NULL);
164 }
165
166 HISTORY_REC *command_history_create(const char *name)
167 {
168         HISTORY_REC *rec;
169         
170         rec = g_new0(HISTORY_REC, 1);
171         
172         if (name != NULL)
173                 rec->name = g_strdup(name);
174
175         histories = g_slist_append(histories, rec);
176         
177         return rec;
178 }
179
180 void command_history_destroy(HISTORY_REC *history)
181 {
182         g_return_if_fail(history != NULL);
183
184         /* history->refcount should be 0 here, or somthing is wrong... */
185         g_return_if_fail(history->refcount == 0);
186
187         histories = g_slist_remove(histories, history);
188
189         g_list_foreach(history->list, (GFunc) g_free, NULL);
190         g_list_free(history->list);
191
192         g_free_not_null(history->name);
193         g_free(history);
194 }
195
196 void command_history_link(const char *name)
197 {
198         HISTORY_REC *rec;
199         rec = command_history_find_name(name);
200
201         if (rec == NULL)
202                 rec = command_history_create(name);
203
204         rec->refcount++;
205 }
206
207 void command_history_unlink(const char *name)
208 {
209         HISTORY_REC *rec;
210         rec = command_history_find_name(name);
211
212         if (rec == NULL)
213                 return;
214
215         if (--(rec->refcount) <= 0)
216                 command_history_destroy(rec);
217 }
218
219 static void sig_window_created(WINDOW_REC *window, int automatic)
220 {
221         window->history = command_history_create(NULL);
222 }
223
224 static void sig_window_destroyed(WINDOW_REC *window)
225 {
226         command_history_unlink(window->history_name);
227         command_history_destroy(window->history);
228         g_free_not_null(window->history_name);
229 }
230
231 static void sig_window_history_changed(WINDOW_REC *window, const char *oldname)
232 {
233         command_history_link(window->history_name);
234         command_history_unlink(oldname);
235 }
236
237 static char *special_history_func(const char *text, void *item, int *free_ret)
238 {
239         WINDOW_REC *window;
240         HISTORY_REC *history;
241         GList *tmp;
242         char *findtext, *ret;
243
244         window = item == NULL ? active_win : window_item_window(item);
245
246         findtext = g_strdup_printf("*%s*", text);
247         ret = NULL;
248
249         history = command_history_current(window);
250         for (tmp = history->list; tmp != NULL; tmp = tmp->next) {
251                 const char *line = tmp->data;
252
253                 if (match_wildcards(findtext, line)) {
254                         *free_ret = TRUE;
255                         ret = g_strdup(line);
256                 }
257         }
258         g_free(findtext);
259
260         return ret;
261 }
262
263 static void read_settings(void)
264 {
265         window_history = settings_get_bool("window_history");
266 }
267
268 void command_history_init(void)
269 {
270         settings_add_int("history", "max_command_history", 100);
271         settings_add_bool("history", "window_history", FALSE);
272
273         special_history_func_set(special_history_func);
274
275         global_history = command_history_create(NULL);
276
277         read_settings();
278         signal_add("window created", (SIGNAL_FUNC) sig_window_created);
279         signal_add("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
280         signal_add("window history changed", (SIGNAL_FUNC) sig_window_history_changed);
281         signal_add("setup changed", (SIGNAL_FUNC) read_settings);
282 }
283
284 void command_history_deinit(void)
285 {
286         signal_remove("window created", (SIGNAL_FUNC) sig_window_created);
287         signal_remove("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
288         signal_remove("window history changed", (SIGNAL_FUNC) sig_window_history_changed);
289         signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
290
291         command_history_destroy(global_history);
292 }