1/* fc.c, created from fc.def. */
2#line 23 "fc.def"
3
4#line 47 "fc.def"
5
6#include <config.h>
7
8#if defined (HISTORY)
9#ifndef _MINIX
10#  include <sys/param.h>
11#endif
12#include "../bashtypes.h"
13#include "posixstat.h"
14#if ! defined(_MINIX) && defined (HAVE_SYS_FILE_H)
15#  include <sys/file.h>
16#endif
17
18#if defined (HAVE_UNISTD_H)
19#  include <unistd.h>
20#endif
21
22#include <stdio.h>
23#include <chartypes.h>
24
25#include "../bashansi.h"
26#include "../bashintl.h"
27#include <errno.h>
28
29#include "../shell.h"
30#include "../builtins.h"
31#include "../flags.h"
32#include "../bashhist.h"
33#include "maxpath.h"
34#include <readline/history.h>
35#include "bashgetopt.h"
36#include "common.h"
37
38#if !defined (errno)
39extern int errno;
40#endif /* !errno */
41
42extern int current_command_line_count;
43extern int literal_history;
44extern int posixly_correct;
45
46extern int unlink __P((const char *));
47
48extern FILE *sh_mktmpfp __P((char *, int, char **));
49extern int delete_last_history __P((void));
50
51/* **************************************************************** */
52/*								    */
53/*	The K*rn shell style fc command (Fix Command)		    */
54/*								    */
55/* **************************************************************** */
56
57/* fc builtin command (fix command) for Bash for those who
58   like K*rn-style history better than csh-style.
59
60     fc [-e ename] [-nlr] [first] [last]
61
62   FIRST and LAST can be numbers specifying the range, or FIRST can be
63   a string, which means the most recent command beginning with that
64   string.
65
66   -e ENAME selects which editor to use.  Default is FCEDIT, then EDITOR,
67      then the editor which corresponds to the current readline editing
68      mode, then vi.
69
70   -l means list lines instead of editing.
71   -n means no line numbers listed.
72   -r means reverse the order of the lines (making it newest listed first).
73
74     fc -e - [pat=rep ...] [command]
75     fc -s [pat=rep ...] [command]
76
77   Equivalent to !command:sg/pat/rep execpt there can be multiple PAT=REP's.
78*/
79
80/* Data structure describing a list of global replacements to perform. */
81typedef struct repl {
82  struct repl *next;
83  char *pat;
84  char *rep;
85} REPL;
86
87/* Accessors for HIST_ENTRY lists that are called HLIST. */
88#define histline(i) (hlist[(i)]->line)
89#define histdata(i) (hlist[(i)]->data)
90
91#define FREE_RLIST() \
92	do { \
93		for (rl = rlist; rl; ) { \
94			REPL *r;	\
95			r = rl->next; \
96			if (rl->pat) \
97				free (rl->pat); \
98			if (rl->rep) \
99				free (rl->rep); \
100			free (rl); \
101			rl = r; \
102		} \
103	} while (0)
104
105static char *fc_dosubs __P((char *, REPL *));
106static char *fc_gethist __P((char *, HIST_ENTRY **));
107static int fc_gethnum __P((char *, HIST_ENTRY **));
108static int fc_number __P((WORD_LIST *));
109static void fc_replhist __P((char *));
110#ifdef INCLUDE_UNUSED
111static char *fc_readline __P((FILE *));
112static void fc_addhist __P((char *));
113#endif
114
115/* String to execute on a file that we want to edit. */
116#define FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-vi}}"
117#if defined (STRICT_POSIX)
118#  define POSIX_FC_EDIT_COMMAND "${FCEDIT:-ed}"
119#else
120#  define POSIX_FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-ed}}"
121#endif
122
123int
124fc_builtin (list)
125     WORD_LIST *list;
126{
127  register int i;
128  register char *sep;
129  int numbering, reverse, listing, execute;
130  int histbeg, histend, last_hist, retval, opt;
131  FILE *stream;
132  REPL *rlist, *rl;
133  char *ename, *command, *newcom, *fcedit;
134  HIST_ENTRY **hlist;
135  char *fn;
136
137  numbering = 1;
138  reverse = listing = execute = 0;
139  ename = (char *)NULL;
140
141  /* Parse out the options and set which of the two forms we're in. */
142  reset_internal_getopt ();
143  lcurrent = list;		/* XXX */
144  while (fc_number (loptend = lcurrent) == 0 &&
145	 (opt = internal_getopt (list, ":e:lnrs")) != -1)
146    {
147      switch (opt)
148	{
149	case 'n':
150	  numbering = 0;
151	  break;
152
153	case 'l':
154	  listing = 1;
155	  break;
156
157	case 'r':
158	  reverse = 1;
159	  break;
160
161	case 's':
162	  execute = 1;
163	  break;
164
165	case 'e':
166	  ename = list_optarg;
167	  break;
168
169	default:
170	  builtin_usage ();
171	  return (EX_USAGE);
172	}
173    }
174
175  list = loptend;
176
177  if (ename && (*ename == '-') && (ename[1] == '\0'))
178    execute = 1;
179
180  /* The "execute" form of the command (re-run, with possible string
181     substitutions). */
182  if (execute)
183    {
184      rlist = (REPL *)NULL;
185      while (list && ((sep = (char *)strchr (list->word->word, '=')) != NULL))
186	{
187	  *sep++ = '\0';
188	  rl = (REPL *)xmalloc (sizeof (REPL));
189	  rl->next = (REPL *)NULL;
190	  rl->pat = savestring (list->word->word);
191	  rl->rep = savestring (sep);
192
193	  if (rlist == NULL)
194	    rlist = rl;
195	  else
196	    {
197	      rl->next = rlist;
198	      rlist = rl;
199	    }
200	  list = list->next;
201	}
202
203      /* If we have a list of substitutions to do, then reverse it
204	 to get the replacements in the proper order. */
205
206      rlist = REVERSE_LIST (rlist, REPL *);
207
208      hlist = history_list ();
209
210      /* If we still have something in list, it is a command spec.
211	 Otherwise, we use the most recent command in time. */
212      command = fc_gethist (list ? list->word->word : (char *)NULL, hlist);
213
214      if (command == NULL)
215	{
216	  builtin_error (_("no command found"));
217	  if (rlist)
218	    FREE_RLIST ();
219
220	  return (EXECUTION_FAILURE);
221	}
222
223      if (rlist)
224	{
225	  newcom = fc_dosubs (command, rlist);
226	  free (command);
227	  FREE_RLIST ();
228	  command = newcom;
229	}
230
231      fprintf (stderr, "%s\n", command);
232      fc_replhist (command);	/* replace `fc -s' with command */
233      return (parse_and_execute (command, "fc", SEVAL_NOHIST));
234    }
235
236  /* This is the second form of the command (the list-or-edit-and-rerun
237     form). */
238  hlist = history_list ();
239  if (hlist == 0)
240    return (EXECUTION_SUCCESS);
241  for (i = 0; hlist[i]; i++);
242
243  /* With the Bash implementation of history, the current command line
244     ("fc blah..." and so on) is already part of the history list by
245     the time we get to this point.  This just skips over that command
246     and makes the last command that this deals with be the last command
247     the user entered before the fc.  We need to check whether the
248     line was actually added (HISTIGNORE may have caused it to not be),
249     so we check hist_last_line_added. */
250
251  /* "When not  listing, he fc command that caused the editing shall not be
252     entered into the history list." */
253  if (listing == 0 && hist_last_line_added)
254    delete_last_history ();
255
256  last_hist = i - 1 - hist_last_line_added;
257
258  if (list)
259    {
260      histbeg = fc_gethnum (list->word->word, hlist);
261      list = list->next;
262
263      if (list)
264	histend = fc_gethnum (list->word->word, hlist);
265      else
266	histend = listing ? last_hist : histbeg;
267    }
268  else
269    {
270      /* The default for listing is the last 16 history items. */
271      if (listing)
272	{
273	  histend = last_hist;
274	  histbeg = histend - 16 + 1;	/* +1 because loop below uses >= */
275	  if (histbeg < 0)
276	    histbeg = 0;
277	}
278      else
279	/* For editing, it is the last history command. */
280	histbeg = histend = last_hist;
281    }
282
283  /* We print error messages for line specifications out of range. */
284  if ((histbeg < 0) || (histend < 0))
285    {
286      sh_erange ((char *)NULL, _("history specification"));
287      return (EXECUTION_FAILURE);
288    }
289
290  if (histend < histbeg)
291    {
292      i = histend;
293      histend = histbeg;
294      histbeg = i;
295
296      reverse = 1;
297    }
298
299  if (listing)
300    stream = stdout;
301  else
302    {
303      numbering = 0;
304      stream = sh_mktmpfp ("bash-fc", MT_USERANDOM|MT_USETMPDIR, &fn);
305      if (stream == 0)
306	{
307	  builtin_error (_("%s: cannot open temp file: %s"), fn ? fn : "", strerror (errno));
308	  FREE (fn);
309	  return (EXECUTION_FAILURE);
310	}
311    }
312
313  for (i = reverse ? histend : histbeg; reverse ? i >= histbeg : i <= histend; reverse ? i-- : i++)
314    {
315      QUIT;
316      if (numbering)
317	fprintf (stream, "%d", i + history_base);
318      if (listing)
319	{
320	  if (posixly_correct)
321	    fputs ("\t", stream);
322	  else
323	    fprintf (stream, "\t%c", histdata (i) ? '*' : ' ');
324	}
325      fprintf (stream, "%s\n", histline (i));
326    }
327
328  if (listing)
329    return (EXECUTION_SUCCESS);
330
331  fclose (stream);
332
333  /* Now edit the file of commands. */
334  if (ename)
335    {
336      command = (char *)xmalloc (strlen (ename) + strlen (fn) + 2);
337      sprintf (command, "%s %s", ename, fn);
338    }
339  else
340    {
341      fcedit = posixly_correct ? POSIX_FC_EDIT_COMMAND : FC_EDIT_COMMAND;
342      command = (char *)xmalloc (3 + strlen (fcedit) + strlen (fn));
343      sprintf (command, "%s %s", fcedit, fn);
344    }
345  retval = parse_and_execute (command, "fc", SEVAL_NOHIST);
346  if (retval != EXECUTION_SUCCESS)
347    {
348      unlink (fn);
349      free (fn);
350      return (EXECUTION_FAILURE);
351    }
352
353  /* Make sure parse_and_execute doesn't turn this off, even though a
354     call to parse_and_execute farther up the function call stack (e.g.,
355     if this is called by vi_edit_and_execute_command) may have already
356     called bash_history_disable. */
357  remember_on_history = 1;
358
359  /* Turn on the `v' flag while fc_execute_file runs so the commands
360     will be echoed as they are read by the parser. */
361  begin_unwind_frame ("fc builtin");
362  add_unwind_protect ((Function *)xfree, fn);
363  add_unwind_protect (unlink, fn);
364  unwind_protect_int (echo_input_at_read);
365  echo_input_at_read = 1;
366
367  retval = fc_execute_file (fn);
368
369  run_unwind_frame ("fc builtin");
370
371  return (retval);
372}
373
374/* Return 1 if LIST->word->word is a legal number for fc's use. */
375static int
376fc_number (list)
377     WORD_LIST *list;
378{
379  char *s;
380
381  if (list == 0)
382    return 0;
383  s = list->word->word;
384  if (*s == '-')
385    s++;
386  return (legal_number (s, (intmax_t *)NULL));
387}
388
389/* Return an absolute index into HLIST which corresponds to COMMAND.  If
390   COMMAND is a number, then it was specified in relative terms.  If it
391   is a string, then it is the start of a command line present in HLIST. */
392static int
393fc_gethnum (command, hlist)
394     char *command;
395     HIST_ENTRY **hlist;
396{
397  int sign = 1, n, clen;
398  register int i, j;
399  register char *s;
400
401  /* Count history elements. */
402  for (i = 0; hlist[i]; i++);
403
404  /* With the Bash implementation of history, the current command line
405     ("fc blah..." and so on) is already part of the history list by
406     the time we get to this point.  This just skips over that command
407     and makes the last command that this deals with be the last command
408     the user entered before the fc.  We need to check whether the
409     line was actually added (HISTIGNORE may have caused it to not be),
410     so we check hist_last_line_added. */
411  i -= 1 + hist_last_line_added;
412
413  /* No specification defaults to most recent command. */
414  if (command == NULL)
415    return (i);
416
417  /* Otherwise, there is a specification.  It can be a number relative to
418     the current position, or an absolute history number. */
419  s = command;
420
421  /* Handle possible leading minus sign. */
422  if (s && (*s == '-'))
423    {
424      sign = -1;
425      s++;
426    }
427
428  if (s && DIGIT(*s))
429    {
430      n = atoi (s);
431      n *= sign;
432
433      /* If the value is negative or zero, then it is an offset from
434	 the current history item. */
435      if (n < 0)
436	{
437	  n += i + 1;
438	  return (n < 0 ? 0 : n);
439	}
440      else if (n == 0)
441	return (i);
442      else
443	{
444	  n -= history_base;
445	  return (i < n ? i : n);
446	}
447    }
448
449  clen = strlen (command);
450  for (j = i; j >= 0; j--)
451    {
452      if (STREQN (command, histline (j), clen))
453	return (j);
454    }
455  return (-1);
456}
457
458/* Locate the most recent history line which begins with
459   COMMAND in HLIST, and return a malloc()'ed copy of it. */
460static char *
461fc_gethist (command, hlist)
462     char *command;
463     HIST_ENTRY **hlist;
464{
465  int i;
466
467  if (hlist == 0)
468    return ((char *)NULL);
469
470  i = fc_gethnum (command, hlist);
471
472  if (i >= 0)
473    return (savestring (histline (i)));
474  else
475    return ((char *)NULL);
476}
477
478#ifdef INCLUDE_UNUSED
479/* Read the edited history lines from STREAM and return them
480   one at a time.  This can read unlimited length lines.  The
481   caller should free the storage. */
482static char *
483fc_readline (stream)
484     FILE *stream;
485{
486  register int c;
487  int line_len = 0, lindex = 0;
488  char *line = (char *)NULL;
489
490  while ((c = getc (stream)) != EOF)
491    {
492      if ((lindex + 2) >= line_len)
493	line = (char *)xrealloc (line, (line_len += 128));
494
495      if (c == '\n')
496	{
497	  line[lindex++] = '\n';
498	  line[lindex++] = '\0';
499	  return (line);
500	}
501      else
502	line[lindex++] = c;
503    }
504
505  if (!lindex)
506    {
507      if (line)
508	free (line);
509
510      return ((char *)NULL);
511    }
512
513  if (lindex + 2 >= line_len)
514    line = (char *)xrealloc (line, lindex + 3);
515
516  line[lindex++] = '\n';	    /* Finish with newline if none in file */
517  line[lindex++] = '\0';
518  return (line);
519}
520#endif
521
522/* Perform the SUBS on COMMAND.
523   SUBS is a list of substitutions, and COMMAND is a simple string.
524   Return a pointer to a malloc'ed string which contains the substituted
525   command. */
526static char *
527fc_dosubs (command, subs)
528     char *command;
529     REPL *subs;
530{
531  register char *new, *t;
532  register REPL *r;
533
534  for (new = savestring (command), r = subs; r; r = r->next)
535    {
536      t = strsub (new, r->pat, r->rep, 1);
537      free (new);
538      new = t;
539    }
540  return (new);
541}
542
543/* Use `command' to replace the last entry in the history list, which,
544   by this time, is `fc blah...'.  The intent is that the new command
545   become the history entry, and that `fc' should never appear in the
546   history list.  This way you can do `r' to your heart's content. */
547static void
548fc_replhist (command)
549     char *command;
550{
551  int n;
552
553  if (command == 0 || *command == '\0')
554    return;
555
556  n = strlen (command);
557  if (command[n - 1] == '\n')
558    command[n - 1] = '\0';
559
560  if (command && *command)
561    {
562      delete_last_history ();
563      maybe_add_history (command);	/* Obeys HISTCONTROL setting. */
564    }
565}
566
567#ifdef INCLUDE_UNUSED
568/* Add LINE to the history, after removing a single trailing newline. */
569static void
570fc_addhist (line)
571     char *line;
572{
573  register int n;
574
575  if (line == 0 || *line == 0)
576    return;
577
578  n = strlen (line);
579
580  if (line[n - 1] == '\n')
581    line[n - 1] = '\0';
582
583  if (line && *line)
584    maybe_add_history (line);		/* Obeys HISTCONTROL setting. */
585}
586#endif
587
588#endif /* HISTORY */
589