From 772f20abb0a3a0979c440114bf3a1cff5b3cef03 Mon Sep 17 00:00:00 2001
From: cvpcs <root@cvpcs.org>
Date: Wed, 2 Jun 2010 11:02:31 -0500
Subject: initial import of bash 4.1

---
 builtins/fc.def | 665 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 665 insertions(+)
 create mode 100644 builtins/fc.def

(limited to 'builtins/fc.def')

diff --git a/builtins/fc.def b/builtins/fc.def
new file mode 100644
index 0000000..a378d9d
--- /dev/null
+++ b/builtins/fc.def
@@ -0,0 +1,665 @@
+This file is fc.def, from which is created fc.c.
+It implements the builtin "fc" 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 fc.c
+
+$BUILTIN fc
+$FUNCTION fc_builtin
+$DEPENDS_ON HISTORY
+$SHORT_DOC fc [-e ename] [-lnr] [first] [last] or fc -s [pat=rep] [command]
+Display or execute commands from the history list.
+
+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.
+
+Options:
+  -e ENAME	select which editor to use.  Default is FCEDIT, then EDITOR,
+		then vi
+  -l 	list lines instead of editing
+  -n	omit line numbers when listing
+  -r	reverse the order of the lines (newest listed first)
+
+With the `fc -s [pat=rep ...] [command]' format, COMMAND is
+re-executed after the substitution OLD=NEW is performed.
+
+A useful alias to use with this is r='fc -s', so that typing `r cc'
+runs the last command beginning with `cc' and typing `r' re-executes
+the last command.
+
+Exit Status:
+Returns success or status of executed command; non-zero if an error occurs.
+$END
+
+#include <config.h>
+
+#if defined (HISTORY)
+#ifndef _MINIX
+#  include <sys/param.h>
+#endif
+#include "../bashtypes.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 "../bashhist.h"
+#include "maxpath.h"
+#include <readline/history.h>
+#include "bashgetopt.h"
+#include "common.h"
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+extern int current_command_line_count;
+extern int literal_history;
+extern int posixly_correct;
+extern int subshell_environment, interactive_shell;
+
+extern int unlink __P((const char *));
+
+extern FILE *sh_mktmpfp __P((char *, int, char **));
+
+/* **************************************************************** */
+/*								    */
+/*	The K*rn shell style fc command (Fix Command)		    */
+/*								    */
+/* **************************************************************** */
+
+/* fc builtin command (fix command) for Bash for those who
+   like K*rn-style history better than csh-style.
+
+     fc [-e ename] [-nlr] [first] [last]
+
+   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.
+
+   -l means list lines instead of editing.
+   -n means no line numbers listed.
+   -r means reverse the order of the lines (making it newest listed first).
+
+     fc -e - [pat=rep ...] [command]
+     fc -s [pat=rep ...] [command]
+
+   Equivalent to !command:sg/pat/rep execpt there can be multiple PAT=REP's.
+*/
+
+/* Data structure describing a list of global replacements to perform. */
+typedef struct repl {
+  struct repl *next;
+  char *pat;
+  char *rep;
+} REPL;
+
+/* Accessors for HIST_ENTRY lists that are called HLIST. */
+#define histline(i) (hlist[(i)]->line)
+#define histdata(i) (hlist[(i)]->data)
+
+#define FREE_RLIST() \
+	do { \
+		for (rl = rlist; rl; ) { \
+			REPL *r;	\
+			r = rl->next; \
+			if (rl->pat) \
+				free (rl->pat); \
+			if (rl->rep) \
+				free (rl->rep); \
+			free (rl); \
+			rl = r; \
+		} \
+	} 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)
+     WORD_LIST *list;
+{
+  register int i;
+  register char *sep;
+  int numbering, reverse, listing, execute;
+  int histbeg, histend, last_hist, retval, opt, rh;
+  FILE *stream;
+  REPL *rlist, *rl;
+  char *ename, *command, *newcom, *fcedit;
+  HIST_ENTRY **hlist;
+  char *fn;
+
+  numbering = 1;
+  reverse = listing = execute = 0;
+  ename = (char *)NULL;
+
+  /* Parse out the options and set which of the two forms we're in. */
+  reset_internal_getopt ();
+  lcurrent = list;		/* XXX */
+  while (fc_number (loptend = lcurrent) == 0 &&
+	 (opt = internal_getopt (list, ":e:lnrs")) != -1)
+    {
+      switch (opt)
+	{
+	case 'n':
+	  numbering = 0;
+	  break;
+
+	case 'l':
+	  listing = 1;
+	  break;
+
+	case 'r':
+	  reverse = 1;
+	  break;
+
+	case 's':
+	  execute = 1;
+	  break;
+
+	case 'e':
+	  ename = list_optarg;
+	  break;
+
+	default:
+	  builtin_usage ();
+	  return (EX_USAGE);
+	}
+    }
+
+  list = loptend;
+
+  if (ename && (*ename == '-') && (ename[1] == '\0'))
+    execute = 1;
+
+  /* The "execute" form of the command (re-run, with possible string
+     substitutions). */
+  if (execute)
+    {
+      rlist = (REPL *)NULL;
+      while (list && ((sep = (char *)strchr (list->word->word, '=')) != NULL))
+	{
+	  *sep++ = '\0';
+	  rl = (REPL *)xmalloc (sizeof (REPL));
+	  rl->next = (REPL *)NULL;
+	  rl->pat = savestring (list->word->word);
+	  rl->rep = savestring (sep);
+
+	  if (rlist == NULL)
+	    rlist = rl;
+	  else
+	    {
+	      rl->next = rlist;
+	      rlist = rl;
+	    }
+	  list = list->next;
+	}
+
+      /* If we have a list of substitutions to do, then reverse it
+	 to get the replacements in the proper order. */
+
+      rlist = REVERSE_LIST (rlist, REPL *);
+
+      hlist = history_list ();
+
+      /* 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);
+
+      if (command == NULL)
+	{
+	  builtin_error (_("no command found"));
+	  if (rlist)
+	    FREE_RLIST ();
+
+	  return (EXECUTION_FAILURE);
+	}
+
+      if (rlist)
+	{
+	  newcom = fc_dosubs (command, rlist);
+	  free (command);
+	  FREE_RLIST ();
+	  command = newcom;
+	}
+
+      fprintf (stderr, "%s\n", command);
+      fc_replhist (command);	/* replace `fc -s' with command */
+      /* Posix says that the re-executed commands should be entered into the
+	 history. */
+      return (parse_and_execute (command, "fc", SEVAL_NOHIST));
+    }
+
+  /* This is the second form of the command (the list-or-edit-and-rerun
+     form). */
+  hlist = history_list ();
+  if (hlist == 0)
+    return (EXECUTION_SUCCESS);
+  for (i = 0; hlist[i]; i++);
+
+  /* With the Bash implementation of history, the current command line
+     ("fc blah..." and so on) is already part of the history list by
+     the time we get to this point.  This just skips over that command
+     and makes the last command that this deals with be the last command
+     the user entered before the fc.  We need to check whether the
+     line was actually added (HISTIGNORE may have caused it to not be),
+     so we check hist_last_line_added. */
+
+  /* Even though command substitution through parse_and_execute turns off
+     remember_on_history, command substitution in a shell when set -o history
+     has been enabled (interactive or not) should use it in the last_hist
+     calculation as if it were on. */
+  rh = remember_on_history || ((subshell_environment & SUBSHELL_COMSUB) && enable_history_list);
+  last_hist = i - rh - hist_last_line_added;
+
+  if (list)
+    {
+      histbeg = fc_gethnum (list->word->word, hlist);
+      list = list->next;
+
+      if (list)
+	histend = fc_gethnum (list->word->word, hlist);
+      else
+	histend = listing ? last_hist : histbeg;
+    }
+  else
+    {
+      /* The default for listing is the last 16 history items. */
+      if (listing)
+	{
+	  histend = last_hist;
+	  histbeg = histend - 16 + 1;	/* +1 because loop below uses >= */
+	  if (histbeg < 0)
+	    histbeg = 0;
+	}
+      else
+	/* For editing, it is the last history command. */
+	histbeg = histend = last_hist;
+    }
+
+  /* "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)
+    {
+      bash_delete_last_history ();
+      /* If we're editing a single command -- the last command in the
+	 history -- and we just removed the dummy command added by
+	 edit_and_execute_command (), we need to check whether or not we
+	 just removed the last command in the history and need to back
+	 the pointer up.  remember_on_history is off because we're running
+	 in parse_and_execute(). */
+      if (histbeg == histend && histend == last_hist && hlist[last_hist] == 0)
+	last_hist = histbeg = --histend;
+    }
+
+  /* We print error messages for line specifications out of range. */
+  if ((histbeg < 0) || (histend < 0))
+    {
+      sh_erange ((char *)NULL, _("history specification"));
+      return (EXECUTION_FAILURE);
+    }
+
+  if (histend < histbeg)
+    {
+      i = histend;
+      histend = histbeg;
+      histbeg = i;
+
+      reverse = 1;
+    }
+
+  if (listing)
+    stream = stdout;
+  else
+    {
+      numbering = 0;
+      stream = sh_mktmpfp ("bash-fc", MT_USERANDOM|MT_USETMPDIR, &fn);
+      if (stream == 0)
+	{
+	  builtin_error (_("%s: cannot open temp file: %s"), fn ? fn : "", strerror (errno));
+	  FREE (fn);
+	  return (EXECUTION_FAILURE);
+	}
+    }
+
+  for (i = reverse ? histend : histbeg; reverse ? i >= histbeg : i <= histend; reverse ? i-- : i++)
+    {
+      QUIT;
+      if (numbering)
+	fprintf (stream, "%d", i + history_base);
+      if (listing)
+	{
+	  if (posixly_correct)
+	    fputs ("\t", stream);
+	  else
+	    fprintf (stream, "\t%c", histdata (i) ? '*' : ' ');
+	}
+      fprintf (stream, "%s\n", histline (i));
+    }
+
+  if (listing)
+    return (sh_chkwrite (EXECUTION_SUCCESS));
+
+  fflush (stream);
+  if (ferror (stream))
+    {
+      sh_wrerror ();
+      fclose (stream);
+      return (EXECUTION_FAILURE);
+    }
+  fclose (stream);
+
+  /* Now edit the file of commands. */
+  if (ename)
+    {
+      command = (char *)xmalloc (strlen (ename) + strlen (fn) + 2);
+      sprintf (command, "%s %s", ename, fn);
+    }
+  else
+    {
+      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);
+    }
+
+  /* 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;
+
+  /* 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;
+    
+  retval = fc_execute_file (fn);
+
+  run_unwind_frame ("fc builtin");
+
+  return (retval);
+}
+
+/* Return 1 if LIST->word->word is a legal number for fc's use. */
+static int
+fc_number (list)
+     WORD_LIST *list;
+{
+  char *s;
+
+  if (list == 0)
+    return 0;
+  s = list->word->word;
+  if (*s == '-')
+    s++;
+  return (legal_number (s, (intmax_t *)NULL));
+}
+
+/* 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. */
+static int
+fc_gethnum (command, hlist)
+     char *command;
+     HIST_ENTRY **hlist;
+{
+  int sign, n, clen, rh;
+  register int i, j;
+  register char *s;
+
+  sign = 1;
+  /* Count history elements. */
+  for (i = 0; hlist[i]; i++);
+
+  /* With the Bash implementation of history, the current command line
+     ("fc blah..." and so on) is already part of the history list by
+     the time we get to this point.  This just skips over that command
+     and makes the last command that this deals with be the last command
+     the user entered before the fc.  We need to check whether the
+     line was actually added (HISTIGNORE may have caused it to not be),
+     so we check hist_last_line_added.  This needs to agree with the
+     calculation of last_hist in fc_builtin above. */
+  /* Even though command substitution through parse_and_execute turns off
+     remember_on_history, command substitution in a shell when set -o history
+     has been enabled (interactive or not) should use it in the last_hist
+     calculation as if it were on. */
+  rh = remember_on_history || ((subshell_environment & SUBSHELL_COMSUB) && enable_history_list);
+  i -= rh + hist_last_line_added;
+
+  /* No specification defaults to most recent command. */
+  if (command == NULL)
+    return (i);
+
+  /* Otherwise, there is a specification.  It can be a number relative to
+     the current position, or an absolute history number. */
+  s = command;
+
+  /* Handle possible leading minus sign. */
+  if (s && (*s == '-'))
+    {
+      sign = -1;
+      s++;
+    }
+
+  if (s && DIGIT(*s))
+    {
+      n = atoi (s);
+      n *= sign;
+
+      /* If the value is negative or zero, then it is an offset from
+	 the current history item. */
+      if (n < 0)
+	{
+	  n += i + 1;
+	  return (n < 0 ? 0 : n);
+	}
+      else if (n == 0)
+	return (i);
+      else
+	{
+	  n -= history_base;
+	  return (i < n ? i : n);
+	}
+    }
+
+  clen = strlen (command);
+  for (j = i; j >= 0; j--)
+    {
+      if (STREQN (command, histline (j), clen))
+	return (j);
+    }
+  return (-1);
+}
+
+/* Locate the most recent history line which begins with
+   COMMAND in HLIST, and return a malloc()'ed copy of it. */
+static char *
+fc_gethist (command, hlist)
+     char *command;
+     HIST_ENTRY **hlist;
+{
+  int i;
+
+  if (hlist == 0)
+    return ((char *)NULL);
+
+  i = fc_gethnum (command, hlist);
+
+  if (i >= 0)
+    return (savestring (histline (i)));
+  else
+    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. */
+static char *
+fc_readline (stream)
+     FILE *stream;
+{
+  register int c;
+  int line_len = 0, lindex = 0;
+  char *line = (char *)NULL;
+
+  while ((c = getc (stream)) != EOF)
+    {
+      if ((lindex + 2) >= line_len)
+	line = (char *)xrealloc (line, (line_len += 128));
+
+      if (c == '\n')
+	{
+	  line[lindex++] = '\n';
+	  line[lindex++] = '\0';
+	  return (line);
+	}
+      else
+	line[lindex++] = c;
+    }
+
+  if (!lindex)
+    {
+      if (line)
+	free (line);
+
+      return ((char *)NULL);
+    }
+
+  if (lindex + 2 >= line_len)
+    line = (char *)xrealloc (line, lindex + 3);
+
+  line[lindex++] = '\n';	    /* Finish with newline if none in file */
+  line[lindex++] = '\0';
+  return (line);
+}
+#endif
+
+/* Perform the SUBS on COMMAND.
+   SUBS is a list of substitutions, and COMMAND is a simple string.
+   Return a pointer to a malloc'ed string which contains the substituted
+   command. */
+static char *
+fc_dosubs (command, subs)
+     char *command;
+     REPL *subs;
+{
+  register char *new, *t;
+  register REPL *r;
+
+  for (new = savestring (command), r = subs; r; r = r->next)
+    {
+      t = strsub (new, r->pat, r->rep, 1);
+      free (new);
+      new = t;
+    }
+  return (new);
+}
+
+/* Use `command' to replace the last entry in the history list, which,
+   by this time, is `fc blah...'.  The intent is that the new command
+   become the history entry, and that `fc' should never appear in the
+   history list.  This way you can do `r' to your heart's content. */
+static void
+fc_replhist (command)
+     char *command;
+{
+  int n;
+
+  if (command == 0 || *command == '\0')
+    return;
+
+  n = strlen (command);
+  if (command[n - 1] == '\n')
+    command[n - 1] = '\0';
+
+  if (command && *command)
+    {
+      bash_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)
+     char *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);		/* Obeys HISTCONTROL setting. */
+}
+#endif
+
+#endif /* HISTORY */
-- 
cgit v1.1

