Imported from ../bash-1.14.7.tar.gz.
[platform/upstream/bash.git] / builtins / fc.def
1 This file is fc.def, from which is created fc.c.
2 It implements the builtin "fc" in Bash.
3
4 Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
5
6 This file is part of GNU Bash, the Bourne Again SHell.
7
8 Bash is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 1, or (at your option) any later
11 version.
12
13 Bash is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with Bash; see the file COPYING.  If not, write to the Free Software
20 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 $PRODUCES fc.c
23
24 $BUILTIN fc
25 $FUNCTION fc_builtin
26 $DEPENDS_ON HISTORY
27 $SHORT_DOC fc [-e ename] [-nlr] [first] [last] or fc -s [pat=rep] [cmd]
28
29 FIRST and LAST can be numbers specifying the range, or FIRST can be a
30 string, which means the most recent command beginning with that
31 string.
32
33    -e ENAME selects which editor to use.  Default is FCEDIT, then EDITOR,
34       then the editor which corresponds to the current readline editing
35       mode, then vi.
36
37    -l means list lines instead of editing.
38    -n means no line numbers listed.
39    -r means reverse the order of the lines (making it newest listed first).
40
41 With the `fc -s [pat=rep ...] [command]' format, the command is
42 re-executed after the substitution OLD=NEW is performed.
43
44 A useful alias to use with this is r='fc -s', so that typing `r cc'
45 runs the last command beginning with `cc' and typing `r' re-executes
46 the last command.
47 $END
48
49 #include <stdio.h>
50 #include "../bashansi.h"
51 #include "../shell.h"
52 #if defined (HISTORY)
53 #include <sys/param.h>
54 #include <sys/types.h>
55 #include <sys/stat.h>
56 #include <sys/file.h>
57 #include <errno.h>
58 #include "../builtins.h"
59 #include "../flags.h"
60 #include "../maxpath.h"
61 #include "../bashhist.h"
62 #include <readline/history.h>
63 #include "bashgetopt.h"
64
65 /* Not all systems declare ERRNO in errno.h... and some systems #define it! */
66 #if !defined (errno)
67 extern int errno;
68 #endif /* !errno */
69
70 extern int echo_input_at_read;
71
72 extern int unlink ();
73
74 /* **************************************************************** */
75 /*                                                                  */
76 /*      The K*rn shell style fc command (Fix Command)               */
77 /*                                                                  */
78 /* **************************************************************** */
79
80 /* fc builtin command (fix command) for Bash for those who
81    like K*rn-style history better than csh-style.
82
83      fc [-e ename] [-nlr] [first] [last]
84
85    FIRST and LAST can be numbers specifying the range, or FIRST can be
86    a string, which means the most recent command beginning with that
87    string.
88
89    -e ENAME selects which editor to use.  Default is FCEDIT, then EDITOR,
90       then the editor which corresponds to the current readline editing
91       mode, then vi.
92
93    -l means list lines instead of editing.
94    -n means no line numbers listed.
95    -r means reverse the order of the lines (making it newest listed first).
96
97      fc -e - [pat=rep ...] [command]
98      fc -s [pat=rep ...] [command]
99
100    Equivalent to !command:sg/pat/rep execpt there can be multiple PAT=REP's.
101 */
102
103 static char *fc_dosubs (), *fc_replace (), *fc_gethist (), *fc_readline ();
104 static int fc_gethnum ();
105 static void fc_replhist (), fc_addhist ();
106
107 /* Data structure describing a list of global replacements to perform. */
108 typedef struct repl {
109   struct repl *next;
110   char *pat;
111   char *rep;
112 } REPL;
113
114 #define USAGE   "usage: fc [-e ename] [-nlr] [first] [last] or fc -s [pat=rep] [command]"
115
116 /* Accessors for HIST_ENTRY lists that are called HLIST. */
117 #define histline(i) (hlist[(i)]->line)
118 #define histdata(i) (hlist[(i)]->data)
119
120 #define FREE_RLIST() \
121         do { \
122                 for (rl = rlist; rl; ) { \
123                         REPL *r;        \
124                         r = rl->next; \
125                         if (rl->pat) \
126                                 free (rl->pat); \
127                         if (rl->rep) \
128                                 free (rl->rep); \
129                         free (rl); \
130                         rl = r; \
131                 } \
132         } while (0)
133
134 /* String to execute on a file that we want to edit. */
135 #define FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-vi}}"
136
137 int
138 fc_builtin (list)
139      WORD_LIST *list;
140 {
141   register int i;
142   register char *sep;
143   int numbering, reverse, listing, execute;
144   int histbeg, histend, last_hist, retval, first, opt;
145   FILE *stream;
146   REPL *rlist = (REPL *) NULL, *rl;
147   char *ename = NULL, *command, *newcom, *line;
148   HIST_ENTRY **hlist;
149   char fn[MAXPATHLEN];
150
151   numbering = 1;
152   reverse = listing = execute = 0;
153
154   /* Parse out the options and set which of the two forms we're in. */
155
156   while (list && *list->word->word == '-')
157     {
158       register char *s = &((list->word->word)[1]);
159
160       if (!isletter (*s))
161         break;
162
163       while (opt = *s++)
164         {
165           switch (opt)
166             {
167             case 'n':
168               numbering = 0;
169               break;
170
171             case 'l':
172               listing = 1;
173               break;
174
175             case 'r':
176               reverse = 1;
177               break;
178
179             case 's':
180               execute = 1;
181               break;
182
183             case 'e':
184               list = list->next;
185               if (list == NULL)
186                 {
187                   builtin_error (USAGE);
188                   return (EX_USAGE);
189                 }
190               ename = list->word->word;
191               break;
192
193             default:
194               builtin_error (USAGE);
195               return (EX_USAGE);
196             }
197         }
198       list = list->next;
199     }
200
201   if (ename && (*ename == '-') && (ename[1] == '\0'))
202     execute = 1;
203
204   /* The "execute" form of the command (re-run, with possible string
205      substitutions). */
206   if (execute)
207     {
208       while (list && ((sep = (char *)strchr (list->word->word, '=')) != NULL))
209         {
210           *sep++ = '\0';
211           rl = (REPL *)xmalloc (sizeof (REPL));
212           rl->next = (REPL *)NULL;
213           rl->pat = savestring (list->word->word);
214           rl->rep = savestring (sep);
215
216           if (rlist == NULL)
217             rlist = rl;
218           else
219             {
220               rl->next = rlist;
221               rlist = rl;
222             }
223           list = list->next;
224         }
225
226       /* If we have a list of substitutions to do, then reverse it
227          to get the replacements in the proper order. */
228
229       if (rlist && rlist->next)
230         rlist = (REPL *) reverse_list ((GENERIC_LIST *) rlist);
231
232       hlist = history_list ();
233
234       /* If we still have something in list, it is a command spec.
235          Otherwise, we use the most recent command in time. */
236       if (list)
237         command = fc_gethist (list->word->word, hlist);
238       else
239         command = fc_gethist ((char *) NULL, hlist);
240
241       if (command == NULL)
242         {
243           builtin_error ("no command found");
244           if (rlist)
245             FREE_RLIST ();
246
247           return (EXECUTION_FAILURE);
248         }
249
250       if (rlist)
251         {
252           newcom = fc_dosubs (command, rlist);
253           free (command);
254           FREE_RLIST ();
255           command = newcom;
256         }
257
258       printf ("%s\n", command);
259       fc_replhist (command);    /* replace `fc -e -' with command */
260       return (parse_and_execute (command, "fc", -1));
261     }
262
263   /* This is the second form of the command (the list-or-edit-and-rerun
264      form). */
265   hlist = history_list ();
266   if (hlist == 0)
267     return (EXECUTION_SUCCESS);
268   for (i = 0; hlist[i]; i++);
269
270   /* With the Bash implementation of history, the current command line
271      ("fc blah..." and so on) is already part of the history list by
272      the time we get to this point.  This just skips over that command
273      and makes the last command that this deals with be the last command
274      the user entered before the fc. */
275
276   last_hist = i - 2;
277
278   if (list)
279     {
280       histbeg = fc_gethnum (list->word->word, hlist);
281       list = list->next;
282
283       if (list)
284         histend = fc_gethnum (list->word->word, hlist);
285       else
286         {
287           if (listing)
288             histend = last_hist;
289           else
290             histend = histbeg;
291         }
292     }
293   else
294     {
295       /* The default for listing is the last 16 history items. */
296       if (listing)
297         {
298           histend = last_hist;
299           histbeg = histend - 16;
300           if (histbeg < 0)
301             histbeg = 0;
302         }
303       else
304         {
305           /* For editing, it is the last history command. */
306           histbeg = histend = last_hist;
307       }
308     }
309
310   /* We print error messages for line specifications out of range. */
311   if ((histbeg < 0) || (histend < 0) ||
312       (histbeg > last_hist) || (histend > last_hist))
313     {
314       builtin_error ("history specification out of range");
315       return (EXECUTION_FAILURE);
316     }
317
318   if (histend < histbeg)
319     {
320       int t = histend;
321
322       histend = histbeg;
323       histbeg = t;
324       reverse = 1;
325     }
326
327   if (listing)
328     stream = stdout;
329   else
330     {
331       numbering = 0;
332       sprintf (fn, "/tmp/bash%d", (int)time ((long *) 0) + (int)getpid ());
333
334       stream = fopen (fn, "w");
335
336       if (!stream)
337         {
338           builtin_error ("cannot open temp file %s", fn);
339           return (EXECUTION_FAILURE);
340         }
341     }
342
343   if (!reverse)
344     {
345       for (i = histbeg; i <= histend; i++)
346         {
347           QUIT;
348           if (numbering)
349             fprintf (stream, "%d", i + history_base);
350           if (listing)
351             fprintf (stream, "\t%c", histdata (i) ? '*' : ' ');
352           fprintf (stream, "%s\n", histline (i));
353         }
354     }
355   else
356     {
357       for (i = histend; i >= histbeg; i--)
358         {
359           QUIT;
360           if (numbering)
361             fprintf (stream, "%d", i + history_base);
362           if (listing)
363             fprintf (stream, "\t%c", histdata (i) ? '*' : ' ');
364           fprintf (stream, "%s\n", histline (i));
365         }
366     }
367
368   if (listing)
369     return (EXECUTION_SUCCESS);
370
371   fclose (stream);
372
373   /* Now edit the file of commands. */
374   if (ename)
375     {
376       command = (char *)xmalloc (strlen (ename) + strlen (fn) + 2);
377       sprintf (command, "%s %s", ename, fn);
378     }
379   else
380     {
381       command = (char *)xmalloc (3 + strlen (FC_EDIT_COMMAND) + strlen (fn));
382       sprintf (command, "%s %s", FC_EDIT_COMMAND, fn);
383     }
384   parse_and_execute (command, "fc", -1);
385
386   /* Now reopen the file and execute the edited commands. */
387
388   stream = fopen (fn, "r");
389
390   if (stream == NULL)
391     {
392       builtin_error ("cannot reopen temp file %s", fn);
393       unlink (fn);
394       return (EXECUTION_FAILURE);
395     }
396
397   retval = EXECUTION_SUCCESS;
398   first = 1;
399
400   /* First, write the commands to the history file.  This will not happen
401      when we call parse_and_execute, since parse_and_execute disables
402      the command line history while it executes. */
403      
404   while ((line = fc_readline (stream)) != NULL)
405     {
406       if (line[0] == '\n')
407         {
408           free (line);
409           continue;             /* Skip blank lines. */
410         }
411
412       if (first)
413         {
414           first = 0;
415           fc_replhist (line);
416         }
417       else
418         fc_addhist (line);
419
420       free (line);
421     }
422   fclose (stream);
423
424   /* Turn on the `v' flag while maybe_execute_file runs so the commands
425      will be echoed as they are read by the parser. */
426   begin_unwind_frame ("fc builtin");
427   add_unwind_protect (unlink, fn);
428   unwind_protect_int (echo_input_at_read);
429   echo_input_at_read = 1;
430     
431   retval = maybe_execute_file (fn, 0);
432
433   run_unwind_frame ("fc builtin");
434
435   return (retval);
436 }
437
438 /* Return an absolute index into HLIST which corresponds to COMMAND.  If
439    COMMAND is a number, then it was specified in relative terms.  If it
440    is a string, then it is the start of a command line present in HLIST. */
441 static int
442 fc_gethnum (command, hlist)
443      char *command;
444      HIST_ENTRY **hlist;
445 {
446   int sign = 1, n, clen;
447   register int i, j;
448   register char *s;
449
450   /* Count history elements. */
451   for (i = 0; hlist[i]; i++);
452
453   /* With the Bash implementation of history, the current command line
454      ("fc blah..." and so on) is already part of the history list by
455      the time we get to this point.  This just skips over that command
456      and makes the last command that this deals with be the last command
457      the user entered before the fc. */
458   i -= 2;
459
460   /* No specification defaults to most recent command. */
461   if (command == NULL)
462     return (i);
463
464   /* Otherwise, there is a specification.  It can be a number relative to
465      the current position, or an absolute history number. */
466   s = command;
467
468   /* Handle possible leading minus sign. */
469   if (s && (*s == '-'))
470     {
471       sign = -1;
472       s++;
473     }
474
475   if (s && digit(*s))
476     {
477       n = atoi (s);
478       n *= sign;
479
480       /* Anything specified greater than the last history element that we
481          deal with is an error. */
482       if (n > i + history_base)
483         return (-1);
484
485       /* If the value is negative or zero, then it is an offset from
486          the current history item. */
487       if (n < 0)
488         return (i + n + 1);
489       else if (n == 0)
490         return (i);
491       else
492         return (n - history_base);
493     }
494
495   clen = strlen (command);
496   for (j = i; j >= 0; j--)
497     {
498       if (STREQN (command, histline (j), clen))
499         return (j);
500     }
501   return (-1);
502 }
503
504 /* Locate the most recent history line which begins with
505    COMMAND in HLIST, and return a malloc()'ed copy of it. */
506 static char *
507 fc_gethist (command, hlist)
508      char *command;
509      HIST_ENTRY **hlist;
510 {
511   int i;
512
513   if (!hlist)
514     return ((char *)NULL);
515
516   i = fc_gethnum (command, hlist);
517
518   if (i >= 0)
519     return (savestring (histline (i)));
520   else
521     return ((char *)NULL);
522 }
523
524 /* Read the edited history lines from STREAM and return them
525    one at a time.  This can read unlimited length lines.  The
526    caller should free the storage. */
527 static char *
528 fc_readline (stream)
529      FILE *stream;
530 {
531   register int c;
532   int line_len = 0, lindex = 0;
533   char *line = (char *)NULL;
534
535   while ((c = getc (stream)) != EOF)
536     {
537       if ((lindex + 2) >= line_len)
538         line = (char *) xrealloc (line, (line_len += 128));
539
540       if (c == '\n')
541         {
542           line[lindex++] = '\n';
543           line[lindex++] = '\0';
544           return (line);
545         }
546       else
547         line[lindex++] = c;
548     }
549
550   if (!lindex)
551     {
552       if (line)
553         free (line);
554
555       return ((char *)NULL);
556     }
557
558   if (lindex + 2 >= line_len)
559     line = (char *)xrealloc (line, lindex + 3);
560
561   line[lindex++] = '\n';            /* Finish with newline if none in file */
562   line[lindex++] = '\0';
563   return (line);
564 }
565
566 /* Perform the SUBS on COMMAND.
567    SUBS is a list of substitutions, and COMMAND is a simple string.
568    Return a pointer to a malloc'ed string which contains the substituted
569    command. */
570 static char *
571 fc_dosubs (command, subs)
572      char *command;
573      REPL *subs;
574 {
575   register char *new = savestring (command);
576   register REPL *r;
577
578   for (r = subs; r; r = r->next)
579     {
580       register char *t;
581
582       t = fc_replace (r->pat, r->rep, new);
583       free (new);
584       new = t;
585     }
586   return (new);
587 }
588
589 /* Replace the occurrences of PAT with REP in COMMAND.
590    This returns a new string; the caller should free it. */
591 static char *
592 fc_replace (pat, rep, command)
593      char *pat, *rep, *command;
594 {
595   register int i;
596   int patlen, replen, templen;
597   char *new, *temp;
598
599   patlen = strlen (pat);
600   replen = strlen (rep);
601
602   temp = savestring (command);
603   templen = strlen (temp);
604   i = 0;
605
606   for (; (i + patlen) <= templen; i++)
607     {
608       if (STREQN (temp + i, pat, patlen))
609         {
610           new = (char *) xmalloc (1 + (replen - patlen) + templen);
611
612           strncpy (new, temp, i);
613           strncpy (new + i, rep, replen);
614           strncpy (new + i + replen,
615                    temp + i + patlen, templen - (i + patlen));
616           new[templen + (replen - patlen)] = '\0'; /* just in case */
617
618           free (temp);
619           temp = new;
620           i += replen;
621           templen = strlen (temp);
622         }
623     }
624   return (temp);
625 }
626
627 /* Use `command' to replace the last entry in the history list, which,
628    by this time, is `fc blah...'.  The intent is that the new command
629    become the history entry, and that `fc' should never appear in the
630    history list.  This way you can do `r' to your heart's content. */
631 static void
632 fc_replhist (command)
633      char *command;
634 {
635   register int i;
636   HIST_ENTRY **hlist, *histent, *discard;
637   char *data;
638   int n;
639
640   if (!command || !*command)
641     return;
642
643   hlist = history_list ();
644
645   if (hlist == NULL)
646     return;
647
648   for (i = 0; hlist[i]; i++);
649   i--;
650
651   /* History_get () takes a parameter that should be
652      offset by history_base. */
653
654   histent = history_get (history_base + i);     /* Don't free this */
655   if (histent == NULL)
656     return;
657
658   n = strlen (command);
659
660   if (command[n - 1] == '\n')
661     command[n - 1] = '\0';
662
663   if (command && *command)
664     {
665       discard = remove_history (i);
666       if (discard)
667         {
668           if (discard->line)
669             free (discard->line);
670           free ((char *) discard);
671         }
672       maybe_add_history (command);      /* Obeys HISTCONTROL setting. */
673     }
674 }
675
676 /* Add LINE to the history, after removing a single trailing newline. */
677 static void
678 fc_addhist (line)
679      char *line;
680 {
681   register int n;
682
683   n = strlen (line);
684
685   if (line[n - 1] == '\n')
686     line[n - 1] = '\0';
687
688   if (line && *line)
689     maybe_add_history (line);
690 }
691 #endif /* HISTORY */