This file is fc.def, from which is created fc.c.
It implements the builtin "fc" in Bash.
-Copyright (C) 1987-2015 Free Software Foundation, Inc.
+Copyright (C) 1987-2020 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
extern int errno;
#endif /* !errno */
-extern int unlink __P((const char *));
+#define HIST_INVALID INT_MIN
+#define HIST_ERANGE INT_MIN+1
+#define HIST_NOTFOUND INT_MIN+2
-extern FILE *sh_mktmpfp __P((char *, int, char **));
+/* Values for the flags argument to fc_gethnum */
+#define HN_LISTING 0x01
+#define HN_FIRST 0x02
+
+extern int unlink PARAMS((const char *));
+
+extern FILE *sh_mktmpfp PARAMS((char *, int, char **));
+
+extern int suppress_debug_trap_verbose;
/* **************************************************************** */
/* */
} \
} while (0)
-static char *fc_dosubs __P((char *, REPL *));
-static char *fc_gethist __P((char *, HIST_ENTRY **));
-static int fc_gethnum __P((char *, HIST_ENTRY **));
-static int fc_number __P((WORD_LIST *));
-static void fc_replhist __P((char *));
+static char *fc_dosubs PARAMS((char *, REPL *));
+static char *fc_gethist PARAMS((char *, HIST_ENTRY **, int));
+static int fc_gethnum PARAMS((char *, HIST_ENTRY **, int));
+static int fc_number PARAMS((WORD_LIST *));
+static void fc_replhist PARAMS((char *));
#ifdef INCLUDE_UNUSED
-static char *fc_readline __P((FILE *));
-static void fc_addhist __P((char *));
+static char *fc_readline PARAMS((FILE *));
+static void fc_addhist PARAMS((char *));
#endif
static void
break;
case 'l':
- listing = 1;
+ listing = HN_LISTING; /* for fc_gethnum */
break;
case 'r':
/* If we still have something in list, it is a command spec.
Otherwise, we use the most recent command in time. */
- command = fc_gethist (list ? list->word->word : (char *)NULL, hlist);
+ command = fc_gethist (list ? list->word->word : (char *)NULL, hlist, 0);
if (command == NULL)
{
while (last_hist >= 0 && hlist[last_hist] == 0)
last_hist--;
if (last_hist < 0)
- {
- sh_erange ((char *)NULL, _("history specification"));
- return (EXECUTION_FAILURE);
- }
+ last_hist = 0; /* per POSIX */
if (list)
{
- histbeg = fc_gethnum (list->word->word, hlist);
+ histbeg = fc_gethnum (list->word->word, hlist, listing|HN_FIRST);
list = list->next;
if (list)
- histend = fc_gethnum (list->word->word, hlist);
+ histend = fc_gethnum (list->word->word, hlist, listing);
else if (histbeg == real_last)
histend = listing ? real_last : histbeg;
else
histbeg = histend = last_hist;
}
+ if (histbeg == HIST_INVALID || histend == HIST_INVALID)
+ {
+ sh_erange ((char *)NULL, _("history specification"));
+ return (EXECUTION_FAILURE);
+ }
+ else if (histbeg == HIST_ERANGE || histend == HIST_ERANGE)
+ {
+ sh_erange ((char *)NULL, _("history specification"));
+ return (EXECUTION_FAILURE);
+ }
+ else if (histbeg == HIST_NOTFOUND || histend == HIST_NOTFOUND)
+ {
+ builtin_error (_("no command found"));
+ return (EXECUTION_FAILURE);
+ }
+
+ /* We don't throw an error for line specifications out of range, per POSIX */
+ if (histbeg < 0)
+ histbeg = 0;
+ if (histend < 0)
+ histend = 0;
+
/* "When not listing, the fc command that caused the editing shall not be
entered into the history list." */
if (listing == 0 && hist_last_line_added)
in parse_and_execute(). */
if (histbeg == histend && histend == last_hist && hlist[last_hist] == 0)
last_hist = histbeg = --histend;
+
+ if (hlist[last_hist] == 0)
+ last_hist--;
+ if (histend >= last_hist)
+ histend = last_hist;
+ else if (histbeg >= last_hist)
+ histbeg = last_hist;
}
- /* We print error messages for line specifications out of range. */
- if ((histbeg < 0) || (histend < 0))
+ if (histbeg == HIST_INVALID || histend == HIST_INVALID)
+ {
+ sh_erange ((char *)NULL, _("history specification"));
+ return (EXECUTION_FAILURE);
+ }
+ else if (histbeg == HIST_ERANGE || histend == HIST_ERANGE)
{
sh_erange ((char *)NULL, _("history specification"));
return (EXECUTION_FAILURE);
}
+ else if (histbeg == HIST_NOTFOUND || histend == HIST_NOTFOUND)
+ {
+ builtin_error (_("no command found"));
+ return (EXECUTION_FAILURE);
+ }
+
+ /* We don't throw an error for line specifications out of range, per POSIX */
+ if (histbeg < 0)
+ histbeg = 0;
+ if (histend < 0)
+ histend = 0;
if (histend < histbeg)
{
}
#if defined (READLINE)
- /* If we're executing as part of a dispatched readline commnand like
+ /* If we're executing as part of a dispatched readline command like
{emacs,vi}_edit_and_execute_command, the readline state will indicate it.
We could remove the partial command from the history, but ksh93 doesn't
so we stay compatible. */
add_unwind_protect (xfree, fn);
add_unwind_protect (unlink, fn);
add_unwind_protect (set_verbose_flag, (char *)NULL);
+ unwind_protect_int (suppress_debug_trap_verbose);
echo_input_at_read = 1;
+ suppress_debug_trap_verbose = 1;
retval = fc_execute_file (fn);
run_unwind_frame ("fc builtin");
/* Return an absolute index into HLIST which corresponds to COMMAND. If
COMMAND is a number, then it was specified in relative terms. If it
- is a string, then it is the start of a command line present in HLIST. */
+ is a string, then it is the start of a command line present in HLIST.
+ MODE includes HN_LISTING if we are listing commands, and does not if we
+ are executing them. If MODE includes HN_FIRST we are looking for the
+ first history number specification. */
static int
-fc_gethnum (command, hlist)
+fc_gethnum (command, hlist, mode)
char *command;
HIST_ENTRY **hlist;
+ int mode;
{
int sign, n, clen, rh;
- register int i, j, last_hist, real_last;
+ register int i, j, last_hist, real_last, listing;
register char *s;
+ listing = mode & HN_LISTING;
sign = 1;
/* Count history elements. */
for (i = 0; hlist[i]; i++);
n = atoi (s);
n *= sign;
+ /* We want to return something that is an offset to HISTORY_BASE. */
+
/* If the value is negative or zero, then it is an offset from
the current history item. */
+ /* We don't use HN_FIRST here, so we don't return different values
+ depending on whether we're looking for the first or last in a
+ pair of range arguments, but nobody else does, either. */
if (n < 0)
{
n += i + 1;
return (n < 0 ? 0 : n);
}
else if (n == 0)
- return ((sign == -1) ? real_last : i);
+ return ((sign == -1) ? (listing ? real_last : HIST_INVALID) : i);
else
{
+ /* If we're out of range (greater than I (last history entry) or
+ less than HISTORY_BASE, we want to return different values
+ based on whether or not we are looking for the first or last
+ value in a desired range of history entries. */
n -= history_base;
- return (i < n ? i : n);
+ if (n < 0)
+ return (mode & HN_FIRST ? 0 : i);
+ else if (n >= i)
+ return (mode & HN_FIRST ? 0 : i);
+ else
+ return n;
}
}
if (STREQN (command, histline (j), clen))
return (j);
}
- return (-1);
+ return (HIST_NOTFOUND);
}
/* Locate the most recent history line which begins with
- COMMAND in HLIST, and return a malloc()'ed copy of it. */
+ COMMAND in HLIST, and return a malloc()'ed copy of it.
+ MODE is 1 if we are listing commands, 0 if we are executing them. */
static char *
-fc_gethist (command, hlist)
+fc_gethist (command, hlist, mode)
char *command;
HIST_ENTRY **hlist;
+ int mode;
{
int i;
if (hlist == 0)
return ((char *)NULL);
- i = fc_gethnum (command, hlist);
+ i = fc_gethnum (command, hlist, mode);
if (i >= 0)
return (savestring (histline (i)));