1This file is history.def, from which is created history.c. 2It implements the builtin "history" in Bash. 3 4Copyright (C) 1987-2020 Free Software Foundation, Inc. 5 6This file is part of GNU Bash, the Bourne Again SHell. 7 8Bash is free software: you can redistribute it and/or modify 9it under the terms of the GNU General Public License as published by 10the Free Software Foundation, either version 3 of the License, or 11(at your option) any later version. 12 13Bash is distributed in the hope that it will be useful, 14but WITHOUT ANY WARRANTY; without even the implied warranty of 15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16GNU General Public License for more details. 17 18You should have received a copy of the GNU General Public License 19along with Bash. If not, see <http://www.gnu.org/licenses/>. 20 21$PRODUCES history.c 22 23$BUILTIN history 24$FUNCTION history_builtin 25$DEPENDS_ON HISTORY 26$SHORT_DOC history [-c] [-d offset] [n] or history -anrw [filename] or history -ps arg [arg...] 27Display or manipulate the history list. 28 29Display the history list with line numbers, prefixing each modified 30entry with a `*'. An argument of N lists only the last N entries. 31 32Options: 33 -c clear the history list by deleting all of the entries 34 -d offset delete the history entry at position OFFSET. Negative 35 offsets count back from the end of the history list 36 37 -a append history lines from this session to the history file 38 -n read all history lines not already read from the history file 39 and append them to the history list 40 -r read the history file and append the contents to the history 41 list 42 -w write the current history to the history file 43 44 -p perform history expansion on each ARG and display the result 45 without storing it in the history list 46 -s append the ARGs to the history list as a single entry 47 48If FILENAME is given, it is used as the history file. Otherwise, 49if HISTFILE has a value, that is used, else ~/.bash_history. 50 51If the HISTTIMEFORMAT variable is set and not null, its value is used 52as a format string for strftime(3) to print the time stamp associated 53with each displayed history entry. No time stamps are printed otherwise. 54 55Exit Status: 56Returns success unless an invalid option is given or an error occurs. 57$END 58 59#include <config.h> 60 61#if defined (HISTORY) 62#include "../bashtypes.h" 63#if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H) 64# include <sys/file.h> 65#endif 66#include "posixstat.h" 67#include "filecntl.h" 68#include <errno.h> 69#include <stdio.h> 70#if defined (HAVE_UNISTD_H) 71# include <unistd.h> 72#endif 73 74#include "../bashansi.h" 75#include "../bashintl.h" 76 77#include "../shell.h" 78#include "../flags.h" 79#include "../parser.h" 80#include "../bashhist.h" 81#include <readline/history.h> 82#include "bashgetopt.h" 83#include "common.h" 84 85#if !defined (errno) 86extern int errno; 87#endif 88 89static char *histtime PARAMS((HIST_ENTRY *, const char *)); 90static int display_history PARAMS((WORD_LIST *)); 91static void push_history PARAMS((WORD_LIST *)); 92static int expand_and_print_history PARAMS((WORD_LIST *)); 93 94#define AFLAG 0x01 95#define RFLAG 0x02 96#define WFLAG 0x04 97#define NFLAG 0x08 98#define SFLAG 0x10 99#define PFLAG 0x20 100#define CFLAG 0x40 101#define DFLAG 0x80 102 103int 104history_builtin (list) 105 WORD_LIST *list; 106{ 107 int flags, opt, result, old_history_lines, obase, ind; 108 char *filename, *delete_arg, *range; 109 intmax_t delete_offset; 110 111 flags = 0; 112 reset_internal_getopt (); 113 while ((opt = internal_getopt (list, "acd:npsrw")) != -1) 114 { 115 switch (opt) 116 { 117 case 'a': 118 flags |= AFLAG; 119 break; 120 case 'c': 121 flags |= CFLAG; 122 break; 123 case 'n': 124 flags |= NFLAG; 125 break; 126 case 'r': 127 flags |= RFLAG; 128 break; 129 case 'w': 130 flags |= WFLAG; 131 break; 132 case 's': 133 flags |= SFLAG; 134 break; 135 case 'd': 136 flags |= DFLAG; 137 delete_arg = list_optarg; 138 break; 139 case 'p': 140#if defined (BANG_HISTORY) 141 flags |= PFLAG; 142#endif 143 break; 144 CASE_HELPOPT; 145 default: 146 builtin_usage (); 147 return (EX_USAGE); 148 } 149 } 150 list = loptend; 151 152 opt = flags & (AFLAG|RFLAG|WFLAG|NFLAG); 153 if (opt && opt != AFLAG && opt != RFLAG && opt != WFLAG && opt != NFLAG) 154 { 155 builtin_error (_("cannot use more than one of -anrw")); 156 return (EXECUTION_FAILURE); 157 } 158 159 /* clear the history, but allow other arguments to add to it again. */ 160 if (flags & CFLAG) 161 { 162 bash_clear_history (); 163 if (list == 0) 164 return (EXECUTION_SUCCESS); 165 } 166 167 if (flags & SFLAG) 168 { 169 if (list) 170 push_history (list); 171 return (EXECUTION_SUCCESS); 172 } 173#if defined (BANG_HISTORY) 174 else if (flags & PFLAG) 175 { 176 if (list) 177 return (expand_and_print_history (list)); 178 return (sh_chkwrite (EXECUTION_SUCCESS)); 179 } 180#endif 181 else if ((flags & DFLAG) && (range = strchr ((delete_arg[0] == '-') ? delete_arg + 1 : delete_arg, '-'))) 182 { 183 intmax_t delete_start, delete_end; 184 *range++ = '\0'; 185 if (legal_number (delete_arg, &delete_start) == 0 || legal_number (range, &delete_end) == 0) 186 { 187 range[-1] = '-'; 188 sh_erange (delete_arg, _("history position")); 189 return (EXECUTION_FAILURE); 190 } 191 if (delete_arg[0] == '-' && delete_start < 0) 192 { 193 /* the_history[history_length] == 0x0, so this is correct */ 194 delete_start += history_length; 195 if (delete_start < history_base) 196 { 197start_error: 198 sh_erange (delete_arg, _("history position")); 199 return (EXECUTION_FAILURE); 200 } 201 } 202 /* numbers as displayed by display_history are offset by history_base */ 203 else if (delete_start > 0) 204 delete_start -= history_base; 205 if (delete_start < 0 || delete_start >= history_length) 206 goto start_error; 207 if (range[0] == '-' && delete_end < 0) 208 { 209 delete_end += history_length; 210 if (delete_end < history_base) 211 { 212range_error: 213 sh_erange (range, _("history position")); 214 return (EXECUTION_FAILURE); 215 } 216 } 217 else if (delete_end > 0) 218 delete_end -= history_base; 219 if (delete_end < 0 || delete_end >= history_length) 220 goto range_error; 221 result = bash_delete_history_range (delete_start, delete_end); 222 if (where_history () > history_length) 223 history_set_pos (history_length); 224 return (result ? EXECUTION_SUCCESS : EXECUTION_FAILURE); 225 } 226 else if (flags & DFLAG) 227 { 228 if (legal_number (delete_arg, &delete_offset) == 0) 229 { 230 sh_erange (delete_arg, _("history position")); 231 return (EXECUTION_FAILURE); 232 } 233 /* check for negative offsets, count back from end of list */ 234 if (delete_arg[0] == '-' && delete_offset < 0) 235 { 236 /* since the_history[history_length] == 0x0, this calculation means 237 that history -d -1 will delete the last history entry, which at 238 this point is the history -d -1 we just added. */ 239 ind = history_length + delete_offset; 240 if (ind < history_base) 241 { 242 sh_erange (delete_arg, _("history position")); 243 return (EXECUTION_FAILURE); 244 } 245 opt = ind + history_base; /* compensate for opt - history_base below */ 246 } 247 else if ((delete_offset < history_base) || (delete_offset >= (history_base + history_length))) 248 { 249 sh_erange (delete_arg, _("history position")); 250 return (EXECUTION_FAILURE); 251 } 252 else 253 opt = delete_offset; 254 255 /* Positive arguments from numbers as displayed by display_history need 256 to be offset by history_base */ 257 result = bash_delete_histent (opt - history_base); 258 /* Since remove_history changes history_length, this can happen if 259 we delete the last history entry. */ 260 if (where_history () > history_length) 261 history_set_pos (history_length); 262 return (result ? EXECUTION_SUCCESS : EXECUTION_FAILURE); 263 } 264 else if ((flags & (AFLAG|RFLAG|NFLAG|WFLAG|CFLAG)) == 0) 265 { 266 result = display_history (list); 267 return (sh_chkwrite (result)); 268 } 269 270 filename = list ? list->word->word : get_string_value ("HISTFILE"); 271 result = EXECUTION_SUCCESS; 272 273#if defined (RESTRICTED_SHELL) 274 if (restricted && strchr (filename, '/')) 275 { 276 sh_restricted (filename); 277 return (EXECUTION_FAILURE); 278 } 279#endif 280 281 if (flags & AFLAG) /* Append session's history to file. */ 282 result = maybe_append_history (filename); 283 else if (flags & WFLAG) /* Write entire history. */ 284 result = write_history (filename); 285 else if (flags & RFLAG) /* Read entire file. */ 286 { 287 result = read_history (filename); 288 history_lines_in_file = history_lines_read_from_file; 289 /* history_lines_in_file = where_history () + history_base - 1; */ 290 } 291 else if (flags & NFLAG) /* Read `new' history from file. */ 292 { 293 /* Read all of the lines in the file that we haven't already read. */ 294 old_history_lines = history_lines_in_file; 295 obase = history_base; 296 297 using_history (); 298 result = read_history_range (filename, history_lines_in_file, -1); 299 using_history (); 300 301 history_lines_in_file = history_lines_read_from_file; 302 /* history_lines_in_file = where_history () + history_base - 1; */ 303 304 /* If we're rewriting the history file at shell exit rather than just 305 appending the lines from this session to it, the question is whether 306 we reset history_lines_this_session to 0, losing any history entries 307 we had before we read the new entries from the history file, or 308 whether we count the new entries we just read from the file as 309 history lines added during this session. 310 Right now, we do the latter. This will cause these history entries 311 to be written to the history file along with any intermediate entries 312 we add when we do a `history -a', but the alternative is losing 313 them altogether. */ 314 if (force_append_history == 0) 315 history_lines_this_session += history_lines_in_file - old_history_lines + 316 history_base - obase; 317 } 318 319 return (result ? EXECUTION_FAILURE : EXECUTION_SUCCESS); 320} 321 322/* Accessors for HIST_ENTRY lists that are called HLIST. */ 323#define histline(i) (hlist[(i)]->line) 324#define histdata(i) (hlist[(i)]->data) 325 326static char * 327histtime (hlist, histtimefmt) 328 HIST_ENTRY *hlist; 329 const char *histtimefmt; 330{ 331 static char timestr[128]; 332 time_t t; 333 struct tm *tm; 334 335 t = history_get_time (hlist); 336 tm = t ? localtime (&t) : 0; 337 if (t && tm) 338 strftime (timestr, sizeof (timestr), histtimefmt, tm); 339 else if (hlist->timestamp && hlist->timestamp[0]) 340 snprintf (timestr, sizeof (timestr), _("%s: invalid timestamp"), 341 (hlist->timestamp[0] == '#') ? hlist->timestamp + 1: hlist->timestamp); 342 else 343 strcpy (timestr, "??"); 344 return timestr; 345} 346 347static int 348display_history (list) 349 WORD_LIST *list; 350{ 351 register int i; 352 intmax_t limit; 353 HIST_ENTRY **hlist; 354 char *histtimefmt, *timestr; 355 356 if (list) 357 { 358 if (get_numeric_arg (list, 0, &limit) == 0) 359 return (EXECUTION_FAILURE); 360 361 if (limit < 0) 362 limit = -limit; 363 } 364 else 365 limit = -1; 366 367 hlist = history_list (); 368 369 if (hlist) 370 { 371 for (i = 0; hlist[i]; i++) 372 ; 373 374 if (0 <= limit && limit < i) 375 i -= limit; 376 else 377 i = 0; 378 379 histtimefmt = get_string_value ("HISTTIMEFORMAT"); 380 381 while (hlist[i]) 382 { 383 QUIT; 384 385 timestr = (histtimefmt && *histtimefmt) ? histtime (hlist[i], histtimefmt) : (char *)NULL; 386 printf ("%5d%c %s%s\n", i + history_base, 387 histdata(i) ? '*' : ' ', 388 ((timestr && *timestr) ? timestr : ""), 389 histline(i)); 390 i++; 391 } 392 } 393 394 return (EXECUTION_SUCCESS); 395} 396 397/* Remove the last entry in the history list and add each argument in 398 LIST to the history. */ 399static void 400push_history (list) 401 WORD_LIST *list; 402{ 403 char *s; 404 405 /* Delete the last history entry if it was a single entry added to the 406 history list (generally the `history -s' itself), or if `history -s' 407 is being used in a compound command and the compound command was 408 added to the history as a single element (command-oriented history). 409 If you don't want history -s to remove the compound command from the 410 history, change #if 0 to #if 1 below. */ 411#if 0 412 if (remember_on_history && hist_last_line_pushed == 0 && 413 hist_last_line_added && bash_delete_last_history () == 0) 414#else 415 if (remember_on_history && hist_last_line_pushed == 0 && 416 (hist_last_line_added || 417 (current_command_line_count > 0 && current_command_first_line_saved && command_oriented_history)) 418 && bash_delete_last_history () == 0) 419#endif 420 return; 421 422 s = string_list (list); 423 /* Call check_add_history with FORCE set to 1 to skip the check against 424 current_command_line_count. If history -s is used in a compound 425 command, the above code will delete the compound command's history 426 entry and this call will add the line to the history as a separate 427 entry. Without FORCE=1, if current_command_line_count were > 1, the 428 line would be appended to the entry before the just-deleted entry. */ 429 check_add_history (s, 1); /* obeys HISTCONTROL, HISTIGNORE */ 430 431 hist_last_line_pushed = 1; /* XXX */ 432 free (s); 433} 434 435#if defined (BANG_HISTORY) 436static int 437expand_and_print_history (list) 438 WORD_LIST *list; 439{ 440 char *s; 441 int r, result; 442 443 if (hist_last_line_pushed == 0 && hist_last_line_added && bash_delete_last_history () == 0) 444 return EXECUTION_FAILURE; 445 result = EXECUTION_SUCCESS; 446 while (list) 447 { 448 r = history_expand (list->word->word, &s); 449 if (r < 0) 450 { 451 builtin_error (_("%s: history expansion failed"), list->word->word); 452 result = EXECUTION_FAILURE; 453 } 454 else 455 { 456 fputs (s, stdout); 457 putchar ('\n'); 458 } 459 FREE (s); 460 list = list->next; 461 } 462 fflush (stdout); 463 return result; 464} 465#endif /* BANG_HISTORY */ 466#endif /* HISTORY */ 467