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