mutt stable branch with some hacks
at master 319 lines 7.3 kB view raw
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 26struct history 27{ 28 char **hist; 29 short cur; 30 short last; 31}; 32 33/* global vars used for the string-history routines */ 34 35static struct history History[HC_LAST]; 36static int OldSize = 0; 37 38#define GET_HISTORY(CLASS) ((CLASS >= HC_LAST) ? NULL : &History[CLASS]) 39 40static 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 + 1, sizeof (char *)); 56 57 h->cur = 0; 58 h->last = 0; 59} 60 61void 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 97static 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 151cleanup: 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 168static 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 209void 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 222void 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; 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) 245 h->last = 0; 246 } 247 } 248 h->cur = h->last; /* reset to the last entry */ 249} 250 251char *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) 261 next = 0; 262 if (h->hist[next] || (next == h->last)) 263 h->cur = next; 264 else 265 h->cur = 0; 266 return (h->hist[h->cur] ? h->hist[h->cur] : ""); 267} 268 269char *mutt_history_prev (history_class_t hclass) 270{ 271 int prev; 272 struct history *h = GET_HISTORY(hclass); 273 274 if (!HistSize || !h) 275 return (""); /* disabled */ 276 277 prev = h->cur - 1; 278 if (prev < 0) 279 { 280 prev = HistSize; 281 while ((prev > 0) && (prev != h->last) && (h->hist[prev] == NULL)) 282 prev--; 283 } 284 if (h->hist[prev] || (prev == h->last)) 285 h->cur = prev; 286 return (h->hist[h->cur] ? h->hist[h->cur] : ""); 287} 288 289void mutt_reset_history_state (history_class_t hclass) 290{ 291 struct history *h = GET_HISTORY(hclass); 292 293 if (!HistSize || !h) 294 return; /* disabled */ 295 296 h->cur = h->last; 297} 298 299int mutt_history_at_scratch (history_class_t hclass) 300{ 301 struct history *h = GET_HISTORY(hclass); 302 303 if (!HistSize || !h) 304 return 0; /* disabled */ 305 306 return h->cur == h->last; 307} 308 309void mutt_history_save_scratch (history_class_t hclass, const char *s) 310{ 311 struct history *h = GET_HISTORY(hclass); 312 313 if (!HistSize || !h) 314 return; /* disabled */ 315 316 /* Don't check if s has a value because the scratch buffer may contain 317 * an old garbage value that should be overwritten */ 318 mutt_str_replace (&h->hist[h->last], s); 319}