This file is fc.def, from which is created fc.c.
It implements the builtin "fc" in Bash.
-Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
+Copyright (C) 1987-2005 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 1, or (at your option) any later
+Software Foundation; either version 2, or (at your option) any later
version.
Bash is distributed in the hope that it will be useful, but WITHOUT ANY
You should have received a copy of the GNU General Public License along
with Bash; see the file COPYING. If not, write to the Free Software
-Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
$PRODUCES fc.c
$FUNCTION fc_builtin
$DEPENDS_ON HISTORY
$SHORT_DOC fc [-e ename] [-nlr] [first] [last] or fc -s [pat=rep] [cmd]
-
+fc is used to list or edit and re-execute commands from the history list.
FIRST and LAST can be numbers specifying the range, or FIRST can be a
string, which means the most recent command beginning with that
string.
-e ENAME selects which editor to use. Default is FCEDIT, then EDITOR,
- then the editor which corresponds to the current readline editing
- mode, then vi.
+ then vi.
-l means list lines instead of editing.
-n means no line numbers listed.
#include <config.h>
#if defined (HISTORY)
-#include <sys/param.h>
+#ifndef _MINIX
+# include <sys/param.h>
+#endif
#include "../bashtypes.h"
-#include "../posixstat.h"
-#include <sys/file.h>
+#include "posixstat.h"
+#if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H)
+# include <sys/file.h>
+#endif
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include <stdio.h>
+#include <chartypes.h>
#include "../bashansi.h"
+#include "../bashintl.h"
#include <errno.h>
#include "../shell.h"
#include "../builtins.h"
#include "../flags.h"
-#include "../maxpath.h"
#include "../bashhist.h"
+#include "maxpath.h"
#include <readline/history.h>
#include "bashgetopt.h"
#include "common.h"
extern int errno;
#endif /* !errno */
-extern int echo_input_at_read;
extern int current_command_line_count;
extern int literal_history;
+extern int posixly_correct;
-extern int unlink ();
+extern int unlink __P((const char *));
-extern int fc_execute_file ();
+extern FILE *sh_mktmpfp __P((char *, int, char **));
+extern int delete_last_history __P((void));
/* **************************************************************** */
/* */
Equivalent to !command:sg/pat/rep execpt there can be multiple PAT=REP's.
*/
-static char *fc_dosubs (), *fc_gethist (), *fc_readline ();
-static int fc_gethnum (), fc_number ();
-static void fc_replhist (), fc_addhist ();
-
/* Data structure describing a list of global replacements to perform. */
typedef struct repl {
struct repl *next;
} \
} 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 *));
+#ifdef INCLUDE_UNUSED
+static char *fc_readline __P((FILE *));
+static void fc_addhist __P((char *));
+#endif
+
/* String to execute on a file that we want to edit. */
#define FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-vi}}"
+#if defined (STRICT_POSIX)
+# define POSIX_FC_EDIT_COMMAND "${FCEDIT:-ed}"
+#else
+# define POSIX_FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-ed}}"
+#endif
int
fc_builtin (list)
register int i;
register char *sep;
int numbering, reverse, listing, execute;
- int histbeg, histend, last_hist, retval, first, opt;
+ int histbeg, histend, last_hist, retval, opt;
FILE *stream;
REPL *rlist, *rl;
- char *ename, *command, *newcom, *line;
+ char *ename, *command, *newcom, *fcedit;
HIST_ENTRY **hlist;
- char fn[64];
+ char *fn;
numbering = 1;
reverse = listing = execute = 0;
/* If we have a list of substitutions to do, then reverse it
to get the replacements in the proper order. */
- if (rlist && rlist->next)
- rlist = (REPL *)reverse_list ((GENERIC_LIST *) rlist);
+ rlist = REVERSE_LIST (rlist, REPL *);
hlist = history_list ();
if (command == NULL)
{
- builtin_error ("no command found");
+ builtin_error (_("no command found"));
if (rlist)
FREE_RLIST ();
line was actually added (HISTIGNORE may have caused it to not be),
so we check hist_last_line_added. */
+ /* "When not listing, he fc command that caused the editing shall not be
+ entered into the history list." */
+ if (listing == 0 && hist_last_line_added)
+ delete_last_history ();
+
last_hist = i - 1 - hist_last_line_added;
if (list)
if (listing)
{
histend = last_hist;
- histbeg = histend - 16;
+ histbeg = histend - 16 + 1; /* +1 because loop below uses >= */
if (histbeg < 0)
histbeg = 0;
}
}
/* We print error messages for line specifications out of range. */
- if ((histbeg < 0) || (histend < 0) ||
- (histbeg > last_hist) || (histend > last_hist))
+ if ((histbeg < 0) || (histend < 0))
{
- builtin_error ("history specification out of range");
+ sh_erange ((char *)NULL, _("history specification"));
return (EXECUTION_FAILURE);
}
else
{
numbering = 0;
- /* XXX - this is raceable */
- sprintf (fn, "/tmp/bash%d", (int)time ((time_t *) 0) + (int)getpid ());
-
- stream = fopen (fn, "w");
-
+ stream = sh_mktmpfp ("bash-fc", MT_USERANDOM|MT_USETMPDIR, &fn);
if (stream == 0)
{
- builtin_error ("cannot open temp file %s", fn);
+ builtin_error (_("%s: cannot open temp file: %s"), fn ? fn : "", strerror (errno));
+ FREE (fn);
return (EXECUTION_FAILURE);
}
}
if (numbering)
fprintf (stream, "%d", i + history_base);
if (listing)
- fprintf (stream, "\t%c", histdata (i) ? '*' : ' ');
+ {
+ if (posixly_correct)
+ fputs ("\t", stream);
+ else
+ fprintf (stream, "\t%c", histdata (i) ? '*' : ' ');
+ }
fprintf (stream, "%s\n", histline (i));
}
}
else
{
- command = (char *)xmalloc (3 + strlen (FC_EDIT_COMMAND) + strlen (fn));
- sprintf (command, "%s %s", FC_EDIT_COMMAND, fn);
+ fcedit = posixly_correct ? POSIX_FC_EDIT_COMMAND : FC_EDIT_COMMAND;
+ command = (char *)xmalloc (3 + strlen (fcedit) + strlen (fn));
+ sprintf (command, "%s %s", fcedit, fn);
}
retval = parse_and_execute (command, "fc", SEVAL_NOHIST);
if (retval != EXECUTION_SUCCESS)
{
unlink (fn);
+ free (fn);
return (EXECUTION_FAILURE);
}
- /* Now reopen the file and execute the edited commands. */
-
- stream = fopen (fn, "r");
-
- if (stream == NULL)
- {
- builtin_error ("cannot reopen temp file %s", fn);
- unlink (fn);
- return (EXECUTION_FAILURE);
- }
-
- retval = EXECUTION_SUCCESS;
- first = 1;
-
-#if 1
/* Make sure parse_and_execute doesn't turn this off, even though a
call to parse_and_execute farther up the function call stack (e.g.,
if this is called by vi_edit_and_execute_command) may have already
called bash_history_disable. */
remember_on_history = 1;
-#else
- /* First, write the commands to the history file. This will not happen
- when we call parse_and_execute, since parse_and_execute disables
- the command line history while it executes. */
-
- opt = current_command_line_count;
- while ((line = fc_readline (stream)) != NULL)
- {
- if (line[0] == '\n')
- {
- free (line);
- continue; /* Skip blank lines. */
- }
-
- if (first)
- {
- first = 0;
- /* If we retrieved only one command from the history file, but we
- read multiple lines from the edited file, and literal_history
- has been set by `shopt', we assume that it was a compound
- command stored with embedded newlines. In this case, we want
- the history code to store it as one command again. */
- if (literal_history && histbeg == histend)
- current_command_line_count = 1;
- fc_replhist (line);
- }
- else
- {
- if (literal_history && histbeg == histend)
- current_command_line_count++;
- fc_addhist (line);
- }
-
- free (line);
- }
- fclose (stream);
- current_command_line_count = opt;
-#endif
/* Turn on the `v' flag while fc_execute_file runs so the commands
will be echoed as they are read by the parser. */
begin_unwind_frame ("fc builtin");
+ add_unwind_protect ((Function *)xfree, fn);
add_unwind_protect (unlink, fn);
unwind_protect_int (echo_input_at_read);
echo_input_at_read = 1;
s = list->word->word;
if (*s == '-')
s++;
- return (legal_number (s, (long *)NULL));
+ return (legal_number (s, (intmax_t *)NULL));
}
/* Return an absolute index into HLIST which corresponds to COMMAND. If
s++;
}
- if (s && digit(*s))
+ if (s && DIGIT(*s))
{
n = atoi (s);
n *= sign;
- /* Anything specified greater than the last history element that we
- deal with is an error. */
- if (n > i + history_base)
- return (-1);
-
/* If the value is negative or zero, then it is an offset from
the current history item. */
if (n < 0)
- return (i + n + 1);
+ {
+ n += i + 1;
+ return (n < 0 ? 0 : n);
+ }
else if (n == 0)
return (i);
else
- return (n - history_base);
+ {
+ n -= history_base;
+ return (i < n ? i : n);
+ }
}
clen = strlen (command);
{
int i;
- if (!hlist)
+ if (hlist == 0)
return ((char *)NULL);
i = fc_gethnum (command, hlist);
return ((char *)NULL);
}
+#ifdef INCLUDE_UNUSED
/* Read the edited history lines from STREAM and return them
one at a time. This can read unlimited length lines. The
caller should free the storage. */
while ((c = getc (stream)) != EOF)
{
if ((lindex + 2) >= line_len)
- line = (char *) xrealloc (line, (line_len += 128));
+ line = (char *)xrealloc (line, (line_len += 128));
if (c == '\n')
{
line[lindex++] = '\0';
return (line);
}
+#endif
/* Perform the SUBS on COMMAND.
SUBS is a list of substitutions, and COMMAND is a simple string.
fc_replhist (command)
char *command;
{
- register int i;
- HIST_ENTRY **hlist, *histent, *discard;
int n;
if (command == 0 || *command == '\0')
return;
- hlist = history_list ();
-
- if (hlist == NULL)
- return;
-
- for (i = 0; hlist[i]; i++);
- i--;
-
- /* History_get () takes a parameter that should be
- offset by history_base. */
-
- histent = history_get (history_base + i); /* Don't free this */
- if (histent == NULL)
- return;
-
n = strlen (command);
-
if (command[n - 1] == '\n')
command[n - 1] = '\0';
if (command && *command)
{
- discard = remove_history (i);
- if (discard)
- {
- FREE (discard->line);
- free ((char *) discard);
- }
+ delete_last_history ();
maybe_add_history (command); /* Obeys HISTCONTROL setting. */
}
}
+#ifdef INCLUDE_UNUSED
/* Add LINE to the history, after removing a single trailing newline. */
static void
fc_addhist (line)
{
register int n;
+ if (line == 0 || *line == 0)
+ return;
+
n = strlen (line);
if (line[n - 1] == '\n')
line[n - 1] = '\0';
if (line && *line)
- maybe_add_history (line);
+ maybe_add_history (line); /* Obeys HISTCONTROL setting. */
}
+#endif
+
#endif /* HISTORY */