On Fri, 13 Aug 2010 15:09:20 +0200
Gabriel Linder <[email protected]> wrote:

> On Fri, 13 Aug 2010 14:30:50 +0200
> Otto Moerbeek <[email protected]> wrote:
> 
> > Take a look at freebsd. They have added editline support to bc.  If
> > (big if) we're going to add command line editing and history, I'd
> > rather take that. 
> 
> I will take a look at their implementation then, thank you.
> 

Here is a new diff with evolutions from FreeBSD added. I did keep a
single fread for real files, though.

Bonus : fixed a bug in their version when el_push'ing lines.



? test
Index: Makefile
===================================================================
RCS file: /cvs/src/usr.bin/bc/Makefile,v
retrieving revision 1.4
diff -u -r1.4 Makefile
--- Makefile    30 Jun 2006 19:02:28 -0000      1.4
+++ Makefile    17 Aug 2010 16:00:05 -0000
@@ -5,6 +5,8 @@
 CPPFLAGS+=     -I. -I${.CURDIR}
 CFLAGS+=       -Wall -Wno-unused
 YFLAGS+=
+LDADD=         -lcurses -ledit
+DPADD=         ${LIBCURSES} ${LIBEDIT}
 
 beforeinstall:
        install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/bc.library \
Index: bc.y
===================================================================
RCS file: /cvs/src/usr.bin/bc/bc.y,v
retrieving revision 1.33
diff -u -r1.33 bc.y
--- bc.y        27 Oct 2009 23:59:36 -0000      1.33
+++ bc.y        17 Aug 2010 16:00:05 -0000
@@ -43,6 +43,7 @@
 #include <stdbool.h>
 #include <string.h>
 #include <unistd.h>
+#include <histedit.h>
 
 #include "extern.h"
 #include "pathnames.h"
@@ -1073,6 +1074,34 @@
        }
 }
 
+/* ARGSUSED */
+static const char *
+prompt(EditLine *el)
+{
+       return "";
+}
+
+static void
+init_editline(void)
+{
+       if (interactive) {
+               /*
+                * Our stdout will be stdin for dc, so we pass stderr as stdout.
+                */
+               if ((el = el_init(__progname, stdin, stderr, stderr)) == NULL)
+                       err(1, "cannot initialise editline");
+               if ((hl = history_init()) == NULL)
+                       err(1, "cannot initialise editline history");
+               history(hl, &hev, H_SETSIZE, 100);
+               el_set(el, EL_HIST, history, hl);
+               el_set(el, EL_PROMPT, prompt);
+               el_set(el, EL_EDITOR, "emacs");
+               el_set(el, EL_TERMINAL, NULL);
+               el_set(el, EL_SIGNAL, 1);
+               el_source(el, NULL);
+       }
+}
+
 int
 main(int argc, char *argv[])
 {
@@ -1129,6 +1158,7 @@
                        dup(p[1]);
                        close(p[0]);
                        close(p[1]);
+                       init_editline();
                } else {
                        close(STDIN_FILENO);
                        dup(p[0]);
Index: extern.h
===================================================================
RCS file: /cvs/src/usr.bin/bc/extern.h,v
retrieving revision 1.6
diff -u -r1.6 extern.h
--- extern.h    18 Mar 2006 20:44:43 -0000      1.6
+++ extern.h    17 Aug 2010 16:00:05 -0000
@@ -23,17 +23,19 @@
        ssize_t store;
 };
 
-int            yylex(void);
-void           yyerror(char *);
-void           fatal(const char *);
-void           abort_line(int);
+int                    yylex(void);
+void                   yyerror(char *);
+void                   fatal(const char *);
 
-extern int     lineno;
-extern char    *yytext;
-extern FILE    *yyin;
-extern int     fileindex;
-extern int     sargc;
-extern char    **sargv;
-extern char    *filename;
-extern char    *cmdexpr;
-bool           interactive;
+extern int             lineno;
+extern char            *yytext;
+extern FILE            *yyin;
+extern int             fileindex;
+extern int             sargc;
+extern char            **sargv;
+extern char            *filename;
+extern char            *cmdexpr;
+extern bool            interactive;
+extern EditLine                *el;
+extern History         *hl;
+extern HistEvent       hev;
Index: scan.l
===================================================================
RCS file: /cvs/src/usr.bin/bc/scan.l,v
retrieving revision 1.23
diff -u -r1.23 scan.l
--- scan.l      27 Oct 2009 23:59:36 -0000      1.23
+++ scan.l      17 Aug 2010 16:00:05 -0000
@@ -18,10 +18,10 @@
  */
 
 #include <err.h>
-#include <signal.h>
 #include <stdbool.h>
 #include <string.h>
 #include <unistd.h>
+#include <histedit.h>
 
 #include "extern.h"
 #include "pathnames.h"
@@ -29,6 +29,9 @@
 
 int            lineno;
 bool           interactive;
+EditLine       *el;
+History                *hl;
+HistEvent      hev;
 
 static char    *strbuf = NULL;
 static size_t  strbuf_sz = 1;
@@ -36,6 +39,10 @@
 
 static void    init_strbuf(void);
 static void    add_str(const char *);
+static int     bc_yyinput(char *, const int);
+
+#define YY_INPUT(buf, result, max_size) \
+       (result = bc_yyinput(buf, max_size))
 
 %}
 
@@ -221,19 +228,6 @@
        strlcat(strbuf, str, strbuf_sz);
 }
 
-/* ARGSUSED */
-void
-abort_line(int sig)
-{
-       static const char str[] = "[\n]P\n";
-       int save_errno;
-
-       save_errno = errno;
-       YY_FLUSH_BUFFER;        /* XXX signal race? */
-       write(STDOUT_FILENO, str, sizeof(str) - 1);
-       errno = save_errno;
-}
-
 int
 yywrap(void)
 {
@@ -271,8 +265,6 @@
        } else if (fileindex == sargc) {
                fileindex++;
                yyin = stdin;
-               if (interactive)
-                       signal(SIGINT, abort_line);
                lineno = 1;
                filename = "stdin";
                return (0);
@@ -280,3 +272,45 @@
        return (1);
 }
 
+static int
+bc_yyinput(char *buf, const int max_size)
+{
+       /*
+        * This function is used to read both math library and user input,
+        * so we have to check for stdin before using editline even in
+        * interactive mode.
+        */
+       if (interactive && yyin == stdin) {
+               /*
+                * el_push'ed lines do not need to be added to history.
+                */
+               static bool was_pushed = false;
+               const char *line;
+               int count;
+               if ((line = el_gets(el, &count)) == NULL || count <= 0) {
+                       if (count == 0) {
+                               return YY_NULL;
+                       }
+                       err(1, "cannot read input line");
+               }
+               if (!was_pushed)
+                       history(hl, &hev, H_ENTER, line);
+               else
+                       was_pushed = false;
+               if (count > max_size) {
+                       el_push(el, (char *)((uintptr_t)line + max_size));
+                       count = max_size;
+                       was_pushed = true;
+               }
+               memcpy(buf, line, count);
+               return count;
+       } else {
+               if (feof(yyin)) {
+                       if (ferror(yyin)) {
+                               YY_FATAL_ERROR("input in flex scanner failed");
+                       }
+                       return YY_NULL;
+               }
+               return fread(buf, sizeof(char), max_size, yyin);
+       }
+}

Reply via email to