blob: c46a3a63ebcc32e93df1532a0ae7d71bd340f506 [file] [log] [blame]
Bram Moolenaard7663c22019-08-06 21:59:57 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * cmdhist.c: Functions for the history of the command-line.
12 */
13
14#include "vim.h"
15
16static histentry_T *(history[HIST_COUNT]) = {NULL, NULL, NULL, NULL, NULL};
17static int hisidx[HIST_COUNT] = {-1, -1, -1, -1, -1}; // lastused entry
18static int hisnum[HIST_COUNT] = {0, 0, 0, 0, 0};
19 // identifying (unique) number of newest history entry
20static int hislen = 0; // actual length of history tables
21
22/*
23 * Return the length of the history tables
24 */
25 int
26get_hislen(void)
27{
28 return hislen;
29}
30
31/*
32 * Return a pointer to a specified history table
33 */
34 histentry_T *
35get_histentry(int hist_type)
36{
37 return history[hist_type];
38}
39
Dominique Pelle748b3082022-01-08 12:41:16 +000040#if defined(FEAT_VIMINFO) || defined(PROTO)
Bram Moolenaard7663c22019-08-06 21:59:57 +020041 void
42set_histentry(int hist_type, histentry_T *entry)
43{
44 history[hist_type] = entry;
45}
Dominique Pelle748b3082022-01-08 12:41:16 +000046#endif
Bram Moolenaard7663c22019-08-06 21:59:57 +020047
48 int *
49get_hisidx(int hist_type)
50{
51 return &hisidx[hist_type];
52}
53
Dominique Pelle748b3082022-01-08 12:41:16 +000054#if defined(FEAT_VIMINFO) || defined(PROTO)
Bram Moolenaard7663c22019-08-06 21:59:57 +020055 int *
56get_hisnum(int hist_type)
57{
58 return &hisnum[hist_type];
59}
Dominique Pelle748b3082022-01-08 12:41:16 +000060#endif
Bram Moolenaard7663c22019-08-06 21:59:57 +020061
62/*
63 * Translate a history character to the associated type number.
64 */
65 int
66hist_char2type(int c)
67{
68 if (c == ':')
69 return HIST_CMD;
70 if (c == '=')
71 return HIST_EXPR;
72 if (c == '@')
73 return HIST_INPUT;
74 if (c == '>')
75 return HIST_DEBUG;
76 return HIST_SEARCH; // must be '?' or '/'
77}
78
79/*
80 * Table of history names.
81 * These names are used in :history and various hist...() functions.
82 * It is sufficient to give the significant prefix of a history name.
83 */
84
85static char *(history_names[]) =
86{
87 "cmd",
88 "search",
89 "expr",
90 "input",
91 "debug",
92 NULL
93};
94
Bram Moolenaard7663c22019-08-06 21:59:57 +020095/*
96 * Function given to ExpandGeneric() to obtain the possible first
97 * arguments of the ":history command.
98 */
99 char_u *
100get_history_arg(expand_T *xp UNUSED, int idx)
101{
Bram Moolenaar5ff595d2022-08-26 22:36:41 +0100102 char *short_names = ":=@>?/";
103 int short_names_count = (int)STRLEN(short_names);
104 int history_name_count = ARRAY_LENGTH(history_names) - 1;
Bram Moolenaard7663c22019-08-06 21:59:57 +0200105
106 if (idx < short_names_count)
107 {
Bram Moolenaar5ff595d2022-08-26 22:36:41 +0100108 xp->xp_buf[0] = (char_u)short_names[idx];
109 xp->xp_buf[1] = NUL;
110 return xp->xp_buf;
Bram Moolenaard7663c22019-08-06 21:59:57 +0200111 }
112 if (idx < short_names_count + history_name_count)
113 return (char_u *)history_names[idx - short_names_count];
114 if (idx == short_names_count + history_name_count)
115 return (char_u *)"all";
116 return NULL;
117}
Bram Moolenaard7663c22019-08-06 21:59:57 +0200118
119/*
120 * init_history() - Initialize the command line history.
121 * Also used to re-allocate the history when the size changes.
122 */
123 void
124init_history(void)
125{
126 int newlen; // new length of history table
127 histentry_T *temp;
128 int i;
129 int j;
130 int type;
131
132 // If size of history table changed, reallocate it
133 newlen = (int)p_hi;
Yegappan Lakshmanan623e94e2022-11-13 18:11:17 +0000134 if (newlen == hislen) // history length didn't change
135 return;
136
137 // history length changed
138 for (type = 0; type < HIST_COUNT; ++type) // adjust the tables
Bram Moolenaard7663c22019-08-06 21:59:57 +0200139 {
Yegappan Lakshmanan623e94e2022-11-13 18:11:17 +0000140 if (newlen)
Bram Moolenaard7663c22019-08-06 21:59:57 +0200141 {
Yegappan Lakshmanan623e94e2022-11-13 18:11:17 +0000142 temp = ALLOC_MULT(histentry_T, newlen);
143 if (temp == NULL) // out of memory!
Bram Moolenaard7663c22019-08-06 21:59:57 +0200144 {
Yegappan Lakshmanan623e94e2022-11-13 18:11:17 +0000145 if (type == 0) // first one: just keep the old length
Bram Moolenaard7663c22019-08-06 21:59:57 +0200146 {
Yegappan Lakshmanan623e94e2022-11-13 18:11:17 +0000147 newlen = hislen;
148 break;
Bram Moolenaard7663c22019-08-06 21:59:57 +0200149 }
Yegappan Lakshmanan623e94e2022-11-13 18:11:17 +0000150 // Already changed one table, now we can only have zero
151 // length for all tables.
152 newlen = 0;
153 type = -1;
154 continue;
Bram Moolenaard7663c22019-08-06 21:59:57 +0200155 }
156 }
Yegappan Lakshmanan623e94e2022-11-13 18:11:17 +0000157 else
158 temp = NULL;
159
160 if (newlen != 0 && temp == NULL)
161 continue;
162
163 if (hisidx[type] < 0) // there are no entries yet
164 {
165 for (i = 0; i < newlen; ++i)
166 clear_hist_entry(&temp[i]);
167 }
168 else if (newlen > hislen) // array becomes bigger
169 {
170 for (i = 0; i <= hisidx[type]; ++i)
171 temp[i] = history[type][i];
172 j = i;
173 for ( ; i <= newlen - (hislen - hisidx[type]); ++i)
174 clear_hist_entry(&temp[i]);
175 for ( ; j < hislen; ++i, ++j)
176 temp[i] = history[type][j];
177 }
178 else // array becomes smaller or 0
179 {
180 j = hisidx[type];
181 for (i = newlen - 1; ; --i)
182 {
183 if (i >= 0) // copy newest entries
184 temp[i] = history[type][j];
185 else // remove older entries
186 vim_free(history[type][j].hisstr);
187 if (--j < 0)
188 j = hislen - 1;
189 if (j == hisidx[type])
190 break;
191 }
192 hisidx[type] = newlen - 1;
193 }
194 vim_free(history[type]);
195 history[type] = temp;
Bram Moolenaard7663c22019-08-06 21:59:57 +0200196 }
Yegappan Lakshmanan623e94e2022-11-13 18:11:17 +0000197 hislen = newlen;
Bram Moolenaard7663c22019-08-06 21:59:57 +0200198}
199
200 void
201clear_hist_entry(histentry_T *hisptr)
202{
203 hisptr->hisnum = 0;
204 hisptr->viminfo = FALSE;
205 hisptr->hisstr = NULL;
206 hisptr->time_set = 0;
207}
208
209/*
210 * Check if command line 'str' is already in history.
211 * If 'move_to_front' is TRUE, matching entry is moved to end of history.
212 */
213 int
214in_history(
215 int type,
216 char_u *str,
217 int move_to_front, // Move the entry to the front if it exists
218 int sep,
219 int writing) // ignore entries read from viminfo
220{
221 int i;
222 int last_i = -1;
223 char_u *p;
224
225 if (hisidx[type] < 0)
226 return FALSE;
227 i = hisidx[type];
228 do
229 {
230 if (history[type][i].hisstr == NULL)
231 return FALSE;
232
233 // For search history, check that the separator character matches as
234 // well.
235 p = history[type][i].hisstr;
236 if (STRCMP(str, p) == 0
237 && !(writing && history[type][i].viminfo)
238 && (type != HIST_SEARCH || sep == p[STRLEN(p) + 1]))
239 {
240 if (!move_to_front)
241 return TRUE;
242 last_i = i;
243 break;
244 }
245 if (--i < 0)
246 i = hislen - 1;
247 } while (i != hisidx[type]);
248
Yegappan Lakshmanan623e94e2022-11-13 18:11:17 +0000249 if (last_i < 0)
250 return FALSE;
251
252 str = history[type][i].hisstr;
253 while (i != hisidx[type])
Bram Moolenaard7663c22019-08-06 21:59:57 +0200254 {
Yegappan Lakshmanan623e94e2022-11-13 18:11:17 +0000255 if (++i >= hislen)
256 i = 0;
257 history[type][last_i] = history[type][i];
258 last_i = i;
Bram Moolenaard7663c22019-08-06 21:59:57 +0200259 }
Yegappan Lakshmanan623e94e2022-11-13 18:11:17 +0000260 history[type][i].hisnum = ++hisnum[type];
261 history[type][i].viminfo = FALSE;
262 history[type][i].hisstr = str;
263 history[type][i].time_set = vim_time();
264 return TRUE;
Bram Moolenaard7663c22019-08-06 21:59:57 +0200265}
266
267/*
268 * Convert history name (from table above) to its HIST_ equivalent.
269 * When "name" is empty, return "cmd" history.
270 * Returns -1 for unknown history name.
271 */
272 static int
273get_histtype(char_u *name)
274{
275 int i;
276 int len = (int)STRLEN(name);
277
278 // No argument: use current history.
279 if (len == 0)
280 return hist_char2type(get_cmdline_firstc());
281
282 for (i = 0; history_names[i] != NULL; ++i)
283 if (STRNICMP(name, history_names[i], len) == 0)
284 return i;
285
286 if (vim_strchr((char_u *)":=@>?/", name[0]) != NULL && name[1] == NUL)
287 return hist_char2type(name[0]);
288
289 return -1;
290}
291
292static int last_maptick = -1; // last seen maptick
293
294/*
295 * Add the given string to the given history. If the string is already in the
296 * history then it is moved to the front. "histype" may be one of he HIST_
297 * values.
298 */
299 void
300add_to_history(
301 int histype,
302 char_u *new_entry,
303 int in_map, // consider maptick when inside a mapping
304 int sep) // separator character used (search hist)
305{
306 histentry_T *hisptr;
307 int len;
308
309 if (hislen == 0) // no history
310 return;
311
Bram Moolenaare1004402020-10-24 20:49:43 +0200312 if ((cmdmod.cmod_flags & CMOD_KEEPPATTERNS) && histype == HIST_SEARCH)
Bram Moolenaard7663c22019-08-06 21:59:57 +0200313 return;
314
315 // Searches inside the same mapping overwrite each other, so that only
316 // the last line is kept. Be careful not to remove a line that was moved
317 // down, only lines that were added.
318 if (histype == HIST_SEARCH && in_map)
319 {
320 if (maptick == last_maptick && hisidx[HIST_SEARCH] >= 0)
321 {
322 // Current line is from the same mapping, remove it
323 hisptr = &history[HIST_SEARCH][hisidx[HIST_SEARCH]];
324 vim_free(hisptr->hisstr);
325 clear_hist_entry(hisptr);
326 --hisnum[histype];
327 if (--hisidx[HIST_SEARCH] < 0)
328 hisidx[HIST_SEARCH] = hislen - 1;
329 }
330 last_maptick = -1;
331 }
Bram Moolenaard7663c22019-08-06 21:59:57 +0200332
Yegappan Lakshmanan623e94e2022-11-13 18:11:17 +0000333 if (in_history(histype, new_entry, TRUE, sep, FALSE))
334 return;
Bram Moolenaard7663c22019-08-06 21:59:57 +0200335
Yegappan Lakshmanan623e94e2022-11-13 18:11:17 +0000336 if (++hisidx[histype] == hislen)
337 hisidx[histype] = 0;
338 hisptr = &history[histype][hisidx[histype]];
339 vim_free(hisptr->hisstr);
340
341 // Store the separator after the NUL of the string.
342 len = (int)STRLEN(new_entry);
343 hisptr->hisstr = vim_strnsave(new_entry, len + 2);
344 if (hisptr->hisstr != NULL)
345 hisptr->hisstr[len + 1] = sep;
346
347 hisptr->hisnum = ++hisnum[histype];
348 hisptr->viminfo = FALSE;
349 hisptr->time_set = vim_time();
350 if (histype == HIST_SEARCH && in_map)
351 last_maptick = maptick;
Bram Moolenaard7663c22019-08-06 21:59:57 +0200352}
353
354#if defined(FEAT_EVAL) || defined(PROTO)
355
356/*
357 * Get identifier of newest history entry.
358 * "histype" may be one of the HIST_ values.
359 */
360 static int
361get_history_idx(int histype)
362{
363 if (hislen == 0 || histype < 0 || histype >= HIST_COUNT
364 || hisidx[histype] < 0)
365 return -1;
366
367 return history[histype][hisidx[histype]].hisnum;
368}
369
370/*
371 * Calculate history index from a number:
372 * num > 0: seen as identifying number of a history entry
373 * num < 0: relative position in history wrt newest entry
374 * "histype" may be one of the HIST_ values.
375 */
376 static int
377calc_hist_idx(int histype, int num)
378{
379 int i;
380 histentry_T *hist;
381 int wrapped = FALSE;
382
383 if (hislen == 0 || histype < 0 || histype >= HIST_COUNT
384 || (i = hisidx[histype]) < 0 || num == 0)
385 return -1;
386
387 hist = history[histype];
388 if (num > 0)
389 {
390 while (hist[i].hisnum > num)
391 if (--i < 0)
392 {
393 if (wrapped)
394 break;
395 i += hislen;
396 wrapped = TRUE;
397 }
Bram Moolenaar8d588cc2020-02-25 21:47:45 +0100398 if (i >= 0 && hist[i].hisnum == num && hist[i].hisstr != NULL)
Bram Moolenaard7663c22019-08-06 21:59:57 +0200399 return i;
400 }
401 else if (-num <= hislen)
402 {
403 i += num + 1;
404 if (i < 0)
405 i += hislen;
406 if (hist[i].hisstr != NULL)
407 return i;
408 }
409 return -1;
410}
411
412/*
413 * Get a history entry by its index.
414 * "histype" may be one of the HIST_ values.
415 */
416 static char_u *
417get_history_entry(int histype, int idx)
418{
419 idx = calc_hist_idx(histype, idx);
420 if (idx >= 0)
421 return history[histype][idx].hisstr;
422 else
423 return (char_u *)"";
424}
425
426/*
427 * Clear all entries of a history.
428 * "histype" may be one of the HIST_ values.
429 */
430 static int
431clr_history(int histype)
432{
433 int i;
434 histentry_T *hisptr;
435
436 if (hislen != 0 && histype >= 0 && histype < HIST_COUNT)
437 {
438 hisptr = history[histype];
439 for (i = hislen; i--;)
440 {
441 vim_free(hisptr->hisstr);
442 clear_hist_entry(hisptr);
443 hisptr++;
444 }
445 hisidx[histype] = -1; // mark history as cleared
446 hisnum[histype] = 0; // reset identifier counter
447 return OK;
448 }
449 return FAIL;
450}
451
452/*
453 * Remove all entries matching {str} from a history.
454 * "histype" may be one of the HIST_ values.
455 */
456 static int
457del_history_entry(int histype, char_u *str)
458{
459 regmatch_T regmatch;
460 histentry_T *hisptr;
461 int idx;
462 int i;
463 int last;
464 int found = FALSE;
465
466 regmatch.regprog = NULL;
467 regmatch.rm_ic = FALSE; // always match case
468 if (hislen != 0
469 && histype >= 0
470 && histype < HIST_COUNT
471 && *str != NUL
472 && (idx = hisidx[histype]) >= 0
473 && (regmatch.regprog = vim_regcomp(str, RE_MAGIC + RE_STRING))
474 != NULL)
475 {
476 i = last = idx;
477 do
478 {
479 hisptr = &history[histype][i];
480 if (hisptr->hisstr == NULL)
481 break;
482 if (vim_regexec(&regmatch, hisptr->hisstr, (colnr_T)0))
483 {
484 found = TRUE;
485 vim_free(hisptr->hisstr);
486 clear_hist_entry(hisptr);
487 }
488 else
489 {
490 if (i != last)
491 {
492 history[histype][last] = *hisptr;
493 clear_hist_entry(hisptr);
494 }
495 if (--last < 0)
496 last += hislen;
497 }
498 if (--i < 0)
499 i += hislen;
500 } while (i != idx);
501 if (history[histype][idx].hisstr == NULL)
502 hisidx[histype] = -1;
503 }
504 vim_regfree(regmatch.regprog);
505 return found;
506}
507
508/*
509 * Remove an indexed entry from a history.
510 * "histype" may be one of the HIST_ values.
511 */
512 static int
513del_history_idx(int histype, int idx)
514{
515 int i, j;
516
517 i = calc_hist_idx(histype, idx);
518 if (i < 0)
519 return FALSE;
520 idx = hisidx[histype];
521 vim_free(history[histype][i].hisstr);
522
523 // When deleting the last added search string in a mapping, reset
524 // last_maptick, so that the last added search string isn't deleted again.
525 if (histype == HIST_SEARCH && maptick == last_maptick && i == idx)
526 last_maptick = -1;
527
528 while (i != idx)
529 {
530 j = (i + 1) % hislen;
531 history[histype][i] = history[histype][j];
532 i = j;
533 }
534 clear_hist_entry(&history[histype][i]);
535 if (--i < 0)
536 i += hislen;
537 hisidx[histype] = i;
538 return TRUE;
539}
540
541/*
542 * "histadd()" function
543 */
544 void
545f_histadd(typval_T *argvars UNUSED, typval_T *rettv)
546{
547 int histype;
548 char_u *str;
549 char_u buf[NUMBUFLEN];
550
551 rettv->vval.v_number = FALSE;
552 if (check_secure())
553 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200554
555 if (in_vim9script()
556 && (check_for_string_arg(argvars, 0) == FAIL
557 || check_for_string_arg(argvars, 1) == FAIL))
558 return;
559
Bram Moolenaard7663c22019-08-06 21:59:57 +0200560 str = tv_get_string_chk(&argvars[0]); // NULL on type error
561 histype = str != NULL ? get_histtype(str) : -1;
Yegappan Lakshmanan623e94e2022-11-13 18:11:17 +0000562 if (histype < 0)
563 return;
564
565 str = tv_get_string_buf(&argvars[1], buf);
566 if (*str == NUL)
567 return;
568
569 init_history();
570 add_to_history(histype, str, FALSE, NUL);
571 rettv->vval.v_number = TRUE;
Bram Moolenaard7663c22019-08-06 21:59:57 +0200572}
573
574/*
575 * "histdel()" function
576 */
577 void
578f_histdel(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
579{
580 int n;
581 char_u buf[NUMBUFLEN];
582 char_u *str;
583
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +0200584 if (in_vim9script()
585 && (check_for_string_arg(argvars, 0) == FAIL
586 || check_for_opt_string_or_number_arg(argvars, 1) == FAIL))
587 return;
588
Bram Moolenaard7663c22019-08-06 21:59:57 +0200589 str = tv_get_string_chk(&argvars[0]); // NULL on type error
590 if (str == NULL)
591 n = 0;
592 else if (argvars[1].v_type == VAR_UNKNOWN)
593 // only one argument: clear entire history
594 n = clr_history(get_histtype(str));
595 else if (argvars[1].v_type == VAR_NUMBER)
596 // index given: remove that entry
597 n = del_history_idx(get_histtype(str),
598 (int)tv_get_number(&argvars[1]));
599 else
600 // string given: remove all matching entries
601 n = del_history_entry(get_histtype(str),
602 tv_get_string_buf(&argvars[1], buf));
603 rettv->vval.v_number = n;
604}
605
606/*
607 * "histget()" function
608 */
609 void
610f_histget(typval_T *argvars UNUSED, typval_T *rettv)
611{
612 int type;
613 int idx;
614 char_u *str;
615
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200616 if (in_vim9script()
617 && (check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200618 || check_for_opt_number_arg(argvars, 1) == FAIL))
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200619 return;
620
Bram Moolenaard7663c22019-08-06 21:59:57 +0200621 str = tv_get_string_chk(&argvars[0]); // NULL on type error
622 if (str == NULL)
623 rettv->vval.v_string = NULL;
624 else
625 {
626 type = get_histtype(str);
627 if (argvars[1].v_type == VAR_UNKNOWN)
628 idx = get_history_idx(type);
629 else
630 idx = (int)tv_get_number_chk(&argvars[1], NULL);
631 // -1 on type error
632 rettv->vval.v_string = vim_strsave(get_history_entry(type, idx));
633 }
634 rettv->v_type = VAR_STRING;
635}
636
637/*
638 * "histnr()" function
639 */
640 void
641f_histnr(typval_T *argvars UNUSED, typval_T *rettv)
642{
643 int i;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200644 char_u *histname;
Bram Moolenaard7663c22019-08-06 21:59:57 +0200645
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200646 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
647 return;
Bram Moolenaard7663c22019-08-06 21:59:57 +0200648
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200649 histname = tv_get_string_chk(&argvars[0]);
Bram Moolenaard7663c22019-08-06 21:59:57 +0200650 i = histname == NULL ? HIST_CMD - 1 : get_histtype(histname);
651 if (i >= HIST_CMD && i < HIST_COUNT)
652 i = get_history_idx(i);
653 else
654 i = -1;
655 rettv->vval.v_number = i;
656}
657#endif // FEAT_EVAL
658
659#if defined(FEAT_CRYPT) || defined(PROTO)
660/*
661 * Very specific function to remove the value in ":set key=val" from the
662 * history.
663 */
664 void
665remove_key_from_history(void)
666{
667 char_u *p;
668 int i;
669
670 i = hisidx[HIST_CMD];
671 if (i < 0)
672 return;
673 p = history[HIST_CMD][i].hisstr;
Yegappan Lakshmanan623e94e2022-11-13 18:11:17 +0000674 if (p == NULL)
675 return;
676
677 for ( ; *p; ++p)
678 if (STRNCMP(p, "key", 3) == 0 && !isalpha(p[3]))
679 {
680 p = vim_strchr(p + 3, '=');
681 if (p == NULL)
682 break;
683 ++p;
684 for (i = 0; p[i] && !VIM_ISWHITE(p[i]); ++i)
685 if (p[i] == '\\' && p[i + 1])
686 ++i;
687 STRMOVE(p, p + i);
688 --p;
689 }
Bram Moolenaard7663c22019-08-06 21:59:57 +0200690}
691#endif
692
693/*
694 * :history command - print a history
695 */
696 void
697ex_history(exarg_T *eap)
698{
699 histentry_T *hist;
700 int histype1 = HIST_CMD;
701 int histype2 = HIST_CMD;
702 int hisidx1 = 1;
703 int hisidx2 = -1;
704 int idx;
705 int i, j, k;
706 char_u *end;
707 char_u *arg = eap->arg;
708
709 if (hislen == 0)
710 {
711 msg(_("'history' option is zero"));
712 return;
713 }
714
715 if (!(VIM_ISDIGIT(*arg) || *arg == '-' || *arg == ','))
716 {
717 end = arg;
718 while (ASCII_ISALPHA(*end)
719 || vim_strchr((char_u *)":=@>/?", *end) != NULL)
720 end++;
721 i = *end;
722 *end = NUL;
723 histype1 = get_histtype(arg);
724 if (histype1 == -1)
725 {
726 if (STRNICMP(arg, "all", STRLEN(arg)) == 0)
727 {
728 histype1 = 0;
729 histype2 = HIST_COUNT-1;
730 }
731 else
732 {
733 *end = i;
Bram Moolenaar74409f62022-01-01 15:58:22 +0000734 semsg(_(e_trailing_characters_str), arg);
Bram Moolenaard7663c22019-08-06 21:59:57 +0200735 return;
736 }
737 }
738 else
739 histype2 = histype1;
740 *end = i;
741 }
742 else
743 end = arg;
744 if (!get_list_range(&end, &hisidx1, &hisidx2) || *end != NUL)
745 {
Bram Moolenaar74409f62022-01-01 15:58:22 +0000746 semsg(_(e_trailing_characters_str), end);
Bram Moolenaard7663c22019-08-06 21:59:57 +0200747 return;
748 }
749
750 for (; !got_int && histype1 <= histype2; ++histype1)
751 {
752 STRCPY(IObuff, "\n # ");
753 STRCAT(STRCAT(IObuff, history_names[histype1]), " history");
754 msg_puts_title((char *)IObuff);
755 idx = hisidx[histype1];
756 hist = history[histype1];
757 j = hisidx1;
758 k = hisidx2;
759 if (j < 0)
760 j = (-j > hislen) ? 0 : hist[(hislen+j+idx+1) % hislen].hisnum;
761 if (k < 0)
762 k = (-k > hislen) ? 0 : hist[(hislen+k+idx+1) % hislen].hisnum;
763 if (idx >= 0 && j <= k)
764 for (i = idx + 1; !got_int; ++i)
765 {
766 if (i == hislen)
767 i = 0;
768 if (hist[i].hisstr != NULL
769 && hist[i].hisnum >= j && hist[i].hisnum <= k)
770 {
771 msg_putchar('\n');
772 sprintf((char *)IObuff, "%c%6d ", i == idx ? '>' : ' ',
773 hist[i].hisnum);
774 if (vim_strsize(hist[i].hisstr) > (int)Columns - 10)
775 trunc_string(hist[i].hisstr, IObuff + STRLEN(IObuff),
776 (int)Columns - 10, IOSIZE - (int)STRLEN(IObuff));
777 else
778 STRCAT(IObuff, hist[i].hisstr);
779 msg_outtrans(IObuff);
780 out_flush();
781 }
782 if (i == idx)
783 break;
784 }
785 }
786}