Subversion Repositories HelenOS

Rev

Rev 2714 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*  $NetBSD: histedit.c,v 1.24 2000/11/06 04:21:14 mycroft Exp $    */
  2.  
  3. /*-
  4.  * Copyright (c) 1993
  5.  *  The Regents of the University of California.  All rights reserved.
  6.  *
  7.  * This code is derived from software contributed to Berkeley by
  8.  * Kenneth Almquist.
  9.  *
  10.  * Redistribution and use in source and binary forms, with or without
  11.  * modification, are permitted provided that the following conditions
  12.  * are met:
  13.  * 1. Redistributions of source code must retain the above copyright
  14.  *    notice, this list of conditions and the following disclaimer.
  15.  * 2. Redistributions in binary form must reproduce the above copyright
  16.  *    notice, this list of conditions and the following disclaimer in the
  17.  *    documentation and/or other materials provided with the distribution.
  18.  * 3. All advertising materials mentioning features or use of this software
  19.  *    must display the following acknowledgement:
  20.  *  This product includes software developed by the University of
  21.  *  California, Berkeley and its contributors.
  22.  * 4. Neither the name of the University nor the names of its contributors
  23.  *    may be used to endorse or promote products derived from this software
  24.  *    without specific prior written permission.
  25.  *
  26.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  27.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  30.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  31.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  32.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  33.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  34.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  35.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  36.  * SUCH DAMAGE.
  37.  */
  38.  
  39. #include <sys/cdefs.h>
  40. #ifndef lint
  41. #if 0
  42. static char sccsid[] = "@(#)histedit.c  8.2 (Berkeley) 5/4/95";
  43. #else
  44. __RCSID("$NetBSD: histedit.c,v 1.24 2000/11/06 04:21:14 mycroft Exp $");
  45. #endif
  46. #endif /* not lint */
  47.  
  48. #include <sys/param.h>
  49. #include <paths.h>
  50. #include <stdio.h>
  51. #include <stdlib.h>
  52. #include <unistd.h>
  53. /*
  54.  * Editline and history functions (and glue).
  55.  */
  56. #include "shell.h"
  57. #include "parser.h"
  58. #include "var.h"
  59. #include "options.h"
  60. #include "main.h"
  61. #include "output.h"
  62. #include "mystring.h"
  63. #include "error.h"
  64. #ifndef SMALL
  65. #include "myhistedit.h"
  66. #include "eval.h"
  67. #include "memalloc.h"
  68.  
  69. #define MAXHISTLOOPS    4   /* max recursions through fc */
  70. #define DEFEDITOR   "ed"    /* default editor *should* be $EDITOR */
  71.  
  72. History *hist;  /* history cookie */
  73. EditLine *el;   /* editline cookie */
  74. int displayhist;
  75. static FILE *el_in, *el_out;
  76.  
  77. STATIC const char *fc_replace (const char *, char *, char *);
  78.  
  79. /*
  80.  * Set history and editing status.  Called whenever the status may
  81.  * have changed (figures out what to do).
  82.  */
  83. void
  84. histedit()
  85. {
  86.  
  87. #define editing (Eflag || Vflag)
  88.  
  89.     if (iflag) {
  90.         if (!hist) {
  91.             /*
  92.              * turn history on
  93.              */
  94.             INTOFF;
  95.             hist = history_init();
  96.             INTON;
  97.  
  98.             if (hist != NULL)
  99.                 sethistsize(histsizeval());
  100.             else
  101.                 out2str("sh: can't initialize history\n");
  102.         }
  103.         if (editing && !el && isatty(0)) { /* && isatty(2) ??? */
  104.             /*
  105.              * turn editing on
  106.              */
  107.             INTOFF;
  108.             if (el_in == NULL)
  109.                 el_in = fdopen(0, "r");
  110.             if (el_out == NULL)
  111.                 el_out = fdopen(2, "w");
  112.             if (el_in == NULL || el_out == NULL)
  113.                 goto bad;
  114.             el = el_init(arg0, el_in, el_out, el_out);
  115.             if (el != NULL) {
  116.                 if (hist)
  117.                     el_set(el, EL_HIST, history, hist);
  118.                 el_set(el, EL_PROMPT, getprompt);
  119.             } else {
  120. bad:
  121.                 out2str("sh: can't initialize editing\n");
  122.             }
  123.             INTON;
  124.         } else if (!editing && el) {
  125.             INTOFF;
  126.             el_end(el);
  127.             el = NULL;
  128.             INTON;
  129.         }
  130.         if (el) {
  131.             if (Vflag)
  132.                 el_set(el, EL_EDITOR, "vi");
  133.             else if (Eflag)
  134.                 el_set(el, EL_EDITOR, "emacs");
  135.             el_source(el, NULL);
  136.         }
  137.     } else {
  138.         INTOFF;
  139.         if (el) {   /* no editing if not interactive */
  140.             el_end(el);
  141.             el = NULL;
  142.         }
  143.         if (hist) {
  144.             history_end(hist);
  145.             hist = NULL;
  146.         }
  147.         INTON;
  148.     }
  149. }
  150.  
  151.  
  152. void
  153. sethistsize(hs)
  154.     const char *hs;
  155. {
  156.     int histsize;
  157.     HistEvent he;
  158.  
  159.     if (hist != NULL) {
  160.         if (hs == NULL || *hs == '\0' ||
  161.            (histsize = atoi(hs)) < 0)
  162.             histsize = 100;
  163.         history(hist, &he, H_SETSIZE, histsize);
  164.     }
  165. }
  166.  
  167. void
  168. setterm(term)
  169.     const char *term;
  170. {
  171.     if (el != NULL && term != NULL)
  172.         if (el_set(el, EL_TERMINAL, term) != 0) {
  173.             outfmt(out2, "sh: Can't set terminal type %s\n", term);
  174.             outfmt(out2, "sh: Using dumb terminal settings.\n");
  175.         }
  176. }
  177.  
  178. /*
  179.  *  This command is provided since POSIX decided to standardize
  180.  *  the Korn shell fc command.  Oh well...
  181.  */
  182. int
  183. histcmd(argc, argv)
  184.     int argc;
  185.     char **argv;
  186. {
  187.     int ch;
  188.     const char *editor = NULL;
  189.     HistEvent he;
  190.     int lflg = 0, nflg = 0, rflg = 0, sflg = 0;
  191.     int i, retval;
  192.     const char *firststr, *laststr;
  193.     int first, last, direction;
  194.     char *pat = NULL, *repl;    /* ksh "fc old=new" crap */
  195.     static int active = 0;
  196.     struct jmploc jmploc;
  197.     struct jmploc *volatile savehandler;
  198.     char editfile[MAXPATHLEN + 1];
  199.     FILE *efp;
  200. #ifdef __GNUC__
  201.     /* Avoid longjmp clobbering */
  202.     (void) &editor;
  203.     (void) &lflg;
  204.     (void) &nflg;
  205.     (void) &rflg;
  206.     (void) &sflg;
  207.     (void) &firststr;
  208.     (void) &laststr;
  209.     (void) &pat;
  210.     (void) &repl;
  211.     (void) &efp;
  212.     (void) &argc;
  213.     (void) &argv;
  214. #endif
  215.  
  216.     if (hist == NULL)
  217.         error("history not active");
  218.  
  219.     if (argc == 1)
  220.         error("missing history argument");
  221.  
  222. #ifdef __GLIBC__
  223.     optind = 1;
  224. #else
  225.     optreset = 1; optind = 1; /* initialize getopt */
  226. #endif
  227.     while (not_fcnumber(argv[optind]) &&
  228.           (ch = getopt(argc, argv, ":e:lnrs")) != -1)
  229.         switch ((char)ch) {
  230.         case 'e':
  231.             editor = optarg;
  232.             break;
  233.         case 'l':
  234.             lflg = 1;
  235.             break;
  236.         case 'n':
  237.             nflg = 1;
  238.             break;
  239.         case 'r':
  240.             rflg = 1;
  241.             break;
  242.         case 's':
  243.             sflg = 1;
  244.             break;
  245.         case ':':
  246.             error("option -%c expects argument", optopt);
  247.             /* NOTREACHED */
  248.         case '?':
  249.         default:
  250.             error("unknown option: -%c", optopt);
  251.             /* NOTREACHED */
  252.         }
  253.     argc -= optind, argv += optind;
  254.  
  255.     /*
  256.      * If executing...
  257.      */
  258.     if (lflg == 0 || editor || sflg) {
  259.         lflg = 0;   /* ignore */
  260.         editfile[0] = '\0';
  261.         /*
  262.          * Catch interrupts to reset active counter and
  263.          * cleanup temp files.
  264.          */
  265.         if (setjmp(jmploc.loc)) {
  266.             active = 0;
  267.             if (*editfile)
  268.                 unlink(editfile);
  269.             handler = savehandler;
  270.             longjmp(handler->loc, 1);
  271.         }
  272.         savehandler = handler;
  273.         handler = &jmploc;
  274.         if (++active > MAXHISTLOOPS) {
  275.             active = 0;
  276.             displayhist = 0;
  277.             error("called recursively too many times");
  278.         }
  279.         /*
  280.          * Set editor.
  281.          */
  282.         if (sflg == 0) {
  283.             if (editor == NULL &&
  284.                 (editor = bltinlookup("FCEDIT", 1)) == NULL &&
  285.                 (editor = bltinlookup("EDITOR", 1)) == NULL)
  286.                 editor = DEFEDITOR;
  287.             if (editor[0] == '-' && editor[1] == '\0') {
  288.                 sflg = 1;   /* no edit */
  289.                 editor = NULL;
  290.             }
  291.         }
  292.     }
  293.  
  294.     /*
  295.      * If executing, parse [old=new] now
  296.      */
  297.     if (lflg == 0 && argc > 0 &&
  298.          ((repl = strchr(argv[0], '=')) != NULL)) {
  299.         pat = argv[0];
  300.         *repl++ = '\0';
  301.         argc--, argv++;
  302.     }
  303.     /*
  304.      * determine [first] and [last]
  305.      */
  306.     switch (argc) {
  307.     case 0:
  308.         firststr = lflg ? "-16" : "-1";
  309.         laststr = "-1";
  310.         break;
  311.     case 1:
  312.         firststr = argv[0];
  313.         laststr = lflg ? "-1" : argv[0];
  314.         break;
  315.     case 2:
  316.         firststr = argv[0];
  317.         laststr = argv[1];
  318.         break;
  319.     default:
  320.         error("too many args");
  321.         /* NOTREACHED */
  322.     }
  323.     /*
  324.      * Turn into event numbers.
  325.      */
  326.     first = str_to_event(firststr, 0);
  327.     last = str_to_event(laststr, 1);
  328.  
  329.     if (rflg) {
  330.         i = last;
  331.         last = first;
  332.         first = i;
  333.     }
  334.     /*
  335.      * XXX - this should not depend on the event numbers
  336.      * always increasing.  Add sequence numbers or offset
  337.      * to the history element in next (diskbased) release.
  338.      */
  339.     direction = first < last ? H_PREV : H_NEXT;
  340.  
  341.     /*
  342.      * If editing, grab a temp file.
  343.      */
  344.     if (editor) {
  345.         int fd;
  346.         INTOFF;     /* easier */
  347.         sprintf(editfile, "%s_shXXXXXX", _PATH_TMP);
  348.         if ((fd = mkstemp(editfile)) < 0)
  349.             error("can't create temporary file %s", editfile);
  350.         if ((efp = fdopen(fd, "w")) == NULL) {
  351.             close(fd);
  352.             error("can't allocate stdio buffer for temp");
  353.         }
  354.     }
  355.  
  356.     /*
  357.      * Loop through selected history events.  If listing or executing,
  358.      * do it now.  Otherwise, put into temp file and call the editor
  359.      * after.
  360.      *
  361.      * The history interface needs rethinking, as the following
  362.      * convolutions will demonstrate.
  363.      */
  364.     history(hist, &he, H_FIRST);
  365.     retval = history(hist, &he, H_NEXT_EVENT, first);
  366.     for (;retval != -1; retval = history(hist, &he, direction)) {
  367.         if (lflg) {
  368.             if (!nflg)
  369.                 out1fmt("%5d ", he.num);
  370.             out1str(he.str);
  371.         } else {
  372.             const char *s = pat ?
  373.                fc_replace(he.str, pat, repl) : he.str;
  374.             char *sp;
  375.  
  376.             if (sflg) {
  377.                 if (displayhist) {
  378.                     out2str(s);
  379.                 }
  380.  
  381.                 evalstring(strcpy(stalloc(strlen(s) + 1), s), 0);
  382.                 free(sp);
  383.                 if (displayhist && hist) {
  384.                     /*
  385.                      *  XXX what about recursive and
  386.                      *  relative histnums.
  387.                      */
  388.                     history(hist, &he, H_ENTER, s);
  389.                 }
  390.             } else
  391.                 fputs(s, efp);
  392.         }
  393.         /*
  394.          * At end?  (if we were to lose last, we'd sure be
  395.          * messed up).
  396.          */
  397.         if (he.num == last)
  398.             break;
  399.     }
  400.     if (editor) {
  401.         char *editcmd;
  402.  
  403.         fclose(efp);
  404.         editcmd = stalloc(strlen(editor) + strlen(editfile) + 2);
  405.         sprintf(editcmd, "%s %s", editor, editfile);
  406.         evalstring(editcmd, 0); /* XXX - should use no JC command */
  407.         INTON;
  408.         readcmdfile(editfile);  /* XXX - should read back - quick tst */
  409.         unlink(editfile);
  410.     }
  411.  
  412.     if (lflg == 0 && active > 0)
  413.         --active;
  414.     if (displayhist)
  415.         displayhist = 0;
  416.     return 0;
  417. }
  418.  
  419. STATIC const char *
  420. fc_replace(s, p, r)
  421.     const char *s;
  422.     char *p, *r;
  423. {
  424.     char *dest;
  425.     int plen = strlen(p);
  426.  
  427.     STARTSTACKSTR(dest);
  428.     while (*s) {
  429.         if (*s == *p && strncmp(s, p, plen) == 0) {
  430.             while (*r)
  431.                 STPUTC(*r++, dest);
  432.             s += plen;
  433.             *p = '\0';  /* so no more matches */
  434.         } else
  435.             STPUTC(*s++, dest);
  436.     }
  437.     STACKSTRNUL(dest);
  438.     dest = grabstackstr(dest);
  439.  
  440.     return (dest);
  441. }
  442.  
  443. int
  444. not_fcnumber(s)
  445.         char *s;
  446. {
  447.     if (s == NULL)
  448.         return 0;
  449.         if (*s == '-')
  450.                 s++;
  451.     return (!is_number(s));
  452. }
  453.  
  454. int
  455. str_to_event(str, last)
  456.     const char *str;
  457.     int last;
  458. {
  459.     HistEvent he;
  460.     const char *s = str;
  461.     int relative = 0;
  462.     int i, retval;
  463.  
  464.     retval = history(hist, &he, H_FIRST);
  465.     switch (*s) {
  466.     case '-':
  467.         relative = 1;
  468.         /*FALLTHROUGH*/
  469.     case '+':
  470.         s++;
  471.     }
  472.     if (is_number(s)) {
  473.         i = atoi(s);
  474.         if (relative) {
  475.             while (retval != -1 && i--) {
  476.                 retval = history(hist, &he, H_NEXT);
  477.             }
  478.             if (retval == -1)
  479.                 retval = history(hist, &he, H_LAST);
  480.         } else {
  481.             retval = history(hist, &he, H_NEXT_EVENT, i);
  482.             if (retval == -1) {
  483.                 /*
  484.                  * the notion of first and last is
  485.                  * backwards to that of the history package
  486.                  */
  487.                 retval = history(hist, &he,
  488.                         last ? H_FIRST : H_LAST);
  489.             }
  490.         }
  491.         if (retval == -1)
  492.             error("history number %s not found (internal error)",
  493.                    str);
  494.     } else {
  495.         /*
  496.          * pattern
  497.          */
  498.         retval = history(hist, &he, H_PREV_STR, str);
  499.         if (retval == -1)
  500.             error("history pattern not found: %s", str);
  501.     }
  502.     return (he.num);
  503. }
  504. #else
  505. int
  506. histcmd(argc, argv)
  507.     int argc;
  508.     char **argv;
  509. {
  510.     error("not compiled with history support");
  511.     /* NOTREACHED */
  512. }
  513. #endif
  514.