]> git.llucax.com Git - software/mutt-debian.git/blob - history.c
Move Mutt with NNTP support to mutt-nntp package
[software/mutt-debian.git] / history.c
1 /*
2  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
3  * 
4  *     This program is free software; you can redistribute it and/or modify
5  *     it under the terms of the GNU General Public License as published by
6  *     the Free Software Foundation; either version 2 of the License, or
7  *     (at your option) any later version.
8  * 
9  *     This program is distributed in the hope that it will be useful,
10  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *     GNU General Public License for more details.
13  * 
14  *     You should have received a copy of the GNU General Public License
15  *     along with this program; if not, write to the Free Software
16  *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */ 
18
19 #if HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #include "mutt.h"
24 #include "history.h"
25
26 struct history
27 {
28   char **hist;
29   short cur;
30   short last;
31 }; 
32
33 /* global vars used for the string-history routines */
34
35 static struct history History[HC_LAST];
36 static int OldSize = 0;
37
38 #define GET_HISTORY(CLASS)      ((CLASS >= HC_LAST) ? NULL : &History[CLASS])
39
40 static void init_history (struct history *h)
41 {
42   int i;
43
44   if(OldSize)
45   {
46     if (h->hist)
47     {
48       for (i = 0 ; i < OldSize ; i ++)
49         FREE (&h->hist[i]);
50       FREE (&h->hist);
51     }
52   }
53   
54   if (HistSize)
55     h->hist = safe_calloc (HistSize, sizeof (char *));
56   
57   h->cur = 0;
58   h->last = 0;
59 }
60
61 void mutt_read_histfile (void)
62 {
63   FILE *f;
64   int line = 0, hclass, read;
65   char *linebuf = NULL, *p;
66   size_t buflen;
67
68   if ((f = fopen (HistFile, "r")) == NULL)
69     return;
70
71   while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line, 0)) != NULL)
72   {
73     read = 0;
74     if (sscanf (linebuf, "%d:%n", &hclass, &read) < 1 || read == 0 ||
75         *(p = linebuf + strlen (linebuf) - 1) != '|' || hclass < 0)
76     {
77       mutt_error (_("Bad history file format (line %d)"), line);
78       break;
79     }
80     /* silently ignore too high class (probably newer mutt) */
81     if (hclass >= HC_LAST)
82       continue;
83     *p = '\0';
84     p = safe_strdup (linebuf + read);
85     if (p)
86     {
87       mutt_convert_string (&p, "utf-8", Charset, 0);
88       mutt_history_add (hclass, p, 0);
89       FREE (&p);
90     }
91   }
92
93   safe_fclose (&f);
94   FREE (&linebuf);
95 }
96
97 static void shrink_histfile (void)
98 {
99   char tmpfname[_POSIX_PATH_MAX];
100   FILE *f, *tmp = NULL;
101   int n[HC_LAST] = { 0 };
102   int line, hclass;
103   char *linebuf = NULL;
104   size_t buflen;
105
106   if ((f = fopen (HistFile, "r")) == NULL)
107     return;
108
109   line = 0;
110   while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line, 0)) != NULL)
111   {
112     if (sscanf (linebuf, "%d", &hclass) < 1 || hclass < 0)
113     {
114       mutt_error (_("Bad history file format (line %d)"), line);
115       goto cleanup;
116     }
117     /* silently ignore too high class (probably newer mutt) */
118     if (hclass >= HC_LAST)
119       continue;
120     n[hclass]++;
121   }
122
123   for(hclass = HC_FIRST; hclass < HC_LAST; hclass++)
124     if (n[hclass] > SaveHist)
125     {
126       mutt_mktemp (tmpfname, sizeof (tmpfname));
127       if ((tmp = safe_fopen (tmpfname, "w+")) == NULL)
128         mutt_perror (tmpfname);
129       break;
130     }
131
132   if (tmp != NULL)
133   {
134     rewind (f);
135     line = 0;
136     while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line, 0)) != NULL)
137     {
138       if (sscanf (linebuf, "%d", &hclass) < 1 || hclass < 0)
139       {
140         mutt_error (_("Bad history file format (line %d)"), line);
141         goto cleanup;
142       }
143       /* silently ignore too high class (probably newer mutt) */
144       if (hclass >= HC_LAST)
145         continue;
146       if (n[hclass]-- <= SaveHist)
147         fprintf (tmp, "%s\n", linebuf);
148     }
149   }
150
151 cleanup:
152   safe_fclose (&f);
153   FREE (&linebuf);
154   if (tmp != NULL)
155   {
156     if (fflush (tmp) == 0 &&
157         (f = fopen (HistFile, "w")) != NULL) /* __FOPEN_CHECKED__ */
158     {
159       rewind (tmp);
160       mutt_copy_stream (tmp, f);
161       safe_fclose (&f);
162     }
163     safe_fclose (&tmp);
164     unlink (tmpfname);
165   }
166 }
167
168 static void save_history (history_class_t hclass, const char *s)
169 {
170   static int n = 0;
171   FILE *f;
172   char *tmp, *p;
173
174   if (!s || !*s)  /* This shouldn't happen, but it's safer. */
175     return;
176
177   if ((f = fopen (HistFile, "a")) == NULL)
178   {
179     mutt_perror ("fopen");
180     return;
181   }
182
183   tmp = safe_strdup (s);
184   mutt_convert_string (&tmp, Charset, "utf-8", 0);
185
186   /* Format of a history item (1 line): "<histclass>:<string>|".
187      We add a '|' in order to avoid lines ending with '\'. */
188   fprintf (f, "%d:", (int) hclass);
189   for (p = tmp; *p; p++)
190   {
191     /* Don't copy \n as a history item must fit on one line. The string
192        shouldn't contain such a character anyway, but as this can happen
193        in practice, we must deal with that. */
194     if (*p != '\n')
195       putc ((unsigned char) *p, f);
196   }
197   fputs ("|\n", f);
198
199   safe_fclose (&f);
200   FREE (&tmp);
201
202   if (--n < 0)
203   {
204     n = SaveHist;
205     shrink_histfile();
206   }
207 }
208
209 void mutt_init_history(void)
210 {
211   history_class_t hclass;
212   
213   if (HistSize == OldSize)
214     return;
215   
216   for(hclass = HC_FIRST; hclass < HC_LAST; hclass++)
217     init_history(&History[hclass]);
218
219   OldSize = HistSize;
220 }
221   
222 void mutt_history_add (history_class_t hclass, const char *s, int save)
223 {
224   int prev;
225   struct history *h = GET_HISTORY(hclass);
226
227   if (!HistSize || !h)
228     return; /* disabled */
229
230   if (*s)
231   {
232     prev = h->last - 1;
233     if (prev < 0) prev = HistSize - 1;
234
235     /* don't add to prompt history:
236      *  - lines beginning by a space
237      *  - repeated lines
238      */
239     if (*s != ' ' && (!h->hist[prev] || mutt_strcmp (h->hist[prev], s) != 0))
240     {
241       if (save && SaveHist)
242         save_history (hclass, s);
243       mutt_str_replace (&h->hist[h->last++], s);
244       if (h->last > HistSize - 1)
245         h->last = 0;
246     }
247   }
248   h->cur = h->last; /* reset to the last entry */
249 }
250
251 char *mutt_history_next (history_class_t hclass)
252 {
253   int next;
254   struct history *h = GET_HISTORY(hclass);
255
256   if (!HistSize || !h)
257     return (""); /* disabled */
258
259   next = h->cur + 1;
260   if (next > HistSize - 1)
261     next = 0;
262   h->cur = h->hist[next] ? next : 0;
263   return (h->hist[h->cur] ? h->hist[h->cur] : "");
264 }
265
266 char *mutt_history_prev (history_class_t hclass)
267 {
268   int prev;
269   struct history *h = GET_HISTORY(hclass);
270
271   if (!HistSize || !h)
272     return (""); /* disabled */
273
274   prev = h->cur - 1;
275   if (prev < 0)
276   {
277     prev = HistSize - 1;
278     while (prev > 0 && h->hist[prev] == NULL)
279       prev--;
280   }
281   if (h->hist[prev])
282     h->cur = prev;
283   return (h->hist[h->cur] ? h->hist[h->cur] : "");
284 }