diff options
Diffstat (limited to 'usr.bin/bc')
-rw-r--r-- | usr.bin/bc/Makefile | 12 | ||||
-rw-r--r-- | usr.bin/bc/bc.1 | 31 | ||||
-rw-r--r-- | usr.bin/bc/bc.library | 6 | ||||
-rw-r--r-- | usr.bin/bc/bc.y | 48 | ||||
-rw-r--r-- | usr.bin/bc/extern.h | 29 | ||||
-rw-r--r-- | usr.bin/bc/scan.l | 216 | ||||
-rw-r--r-- | usr.bin/bc/tty.c | 65 |
7 files changed, 278 insertions, 129 deletions
diff --git a/usr.bin/bc/Makefile b/usr.bin/bc/Makefile index 39d54e0..5fd918d 100644 --- a/usr.bin/bc/Makefile +++ b/usr.bin/bc/Makefile @@ -1,12 +1,12 @@ # $FreeBSD$ -# $OpenBSD: Makefile,v 1.4 2006/06/30 19:02:28 otto Exp $ +# $OpenBSD: Makefile,v 1.7 2013/09/19 16:12:00 otto Exp $ -PROG= bc -SRCS= bc.y scan.l -CFLAGS+= -I. -I${.CURDIR} +PROG= bc +SRCS= bc.y scan.l tty.c +CFLAGS+= -I. -I${.CURDIR} -DPADD= ${LIBEDIT} ${LIBTERMCAP} -LDADD= -ledit -ltermcap +LDADD+= -ledit -lcurses +DPADD+= ${LIBEDIT} ${LIBCURSES} NO_WMISSING_VARIABLE_DECLARATIONS= diff --git a/usr.bin/bc/bc.1 b/usr.bin/bc/bc.1 index 0592acd..2d539ee 100644 --- a/usr.bin/bc/bc.1 +++ b/usr.bin/bc/bc.1 @@ -1,5 +1,5 @@ .\" $FreeBSD$ -.\" $OpenBSD: bc.1,v 1.25 2010/01/02 19:48:56 schwarze Exp $ +.\" $OpenBSD: bc.1,v 1.30 2014/01/14 07:42:42 jmc Exp $ .\" .\" Copyright (C) Caldera International Inc. 2001-2002. .\" All rights reserved. @@ -35,7 +35,7 @@ .\" .\" @(#)bc.1 6.8 (Berkeley) 8/8/91 .\" -.Dd December 6, 2013 +.Dd April 16, 2014 .Dt BC 1 .Os .Sh NAME @@ -140,8 +140,7 @@ The following arithmetic and logical operators can be used. The semantics of the operators is the same as in the C language. They are listed in order of decreasing precedence. Operators in the same group have the same precedence. -.Bl -column -offset indent "= += \-= *= /= %= ^=" "Associativity" \ -"multiply, divide, modulus" +.Bl -column "= += \-= *= /= %= ^=" "Associativity" "multiply, divide, modulus" -offset indent .It Sy "Operator" Ta Sy "Associativity" Ta Sy "Description" .It "++ \-\-" Ta "none" Ta "increment, decrement" .It "\-" Ta "none" Ta "unary minus" @@ -160,7 +159,7 @@ Note the following: .It The relational operators may appear in any expression. The -.St -p1003.2 +.St -p1003.1-2008 standard only allows them in the conditional expression of an .Sq if , .Sq while @@ -342,6 +341,22 @@ $ bc -l -e 'scale = 500; 2 * a(2^10000)' -e quit .Ed .Pp prints an approximation of pi. +.Sh COMMAND LINE EDITING +.Nm +supports interactive command line editing, via the +.Xr editline 3 +library. +It is enabled by default if input is from a tty. +Previous lines can be recalled and edited with the arrow keys, +and other GNU Emacs-style editing keys may be used as well. +.Pp +The +.Xr editline 3 +library is configured with a +.Pa .editrc +file \- refer to +.Xr editrc 5 +for more information. .Sh FILES .Bl -tag -width /usr/share/misc/bc.library -compact .It Pa /usr/share/misc/bc.library @@ -359,9 +374,6 @@ options are no-ops for compatibility with some other implementations of and their use is discouraged. .Sh SEE ALSO .Xr dc 1 -.Pp -"BC \- An Arbitrary Precision Desk-Calculator Language", -.Pa /usr/share/doc/usd/06.bc/ . .Sh STANDARDS The .Nm @@ -370,7 +382,8 @@ utility is compliant with the specification. .Pp The flags -.Op Fl ce +.Op Fl ce , +as well as the parts noted above, are extensions to that specification. .Sh HISTORY The diff --git a/usr.bin/bc/bc.library b/usr.bin/bc/bc.library index 7f4a93e..8b92d25 100644 --- a/usr.bin/bc/bc.library +++ b/usr.bin/bc/bc.library @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $OpenBSD: bc.library,v 1.3 2007/02/03 21:15:06 otto Exp $ */ +/* $OpenBSD: bc.library,v 1.4 2012/03/14 07:35:53 otto Exp $ */ /* * Copyright (C) Caldera International Inc. 2001-2002. @@ -100,13 +100,13 @@ define l(x) { if (x < 1) { s = scale(x) } else { - s = length(x) - scale(x) + s = length(x)-scale(x) } scale = 0 a = (2.31*s)/1 /* estimated integer part of the answer */ s = t + length(a) + 2 /* estimated length of the answer */ while (x > 2) { - scale=0 + scale = 0 scale = (length(x) + scale(x))/2 + 1 if (scale < s) scale = s x = sqrt(x) diff --git a/usr.bin/bc/bc.y b/usr.bin/bc/bc.y index a819e3f..8e7e00c 100644 --- a/usr.bin/bc/bc.y +++ b/usr.bin/bc/bc.y @@ -1,5 +1,5 @@ %{ -/* $OpenBSD: bc.y,v 1.33 2009/10/27 23:59:36 deraadt Exp $ */ +/* $OpenBSD: bc.y,v 1.44 2013/11/20 21:33:54 deraadt Exp $ */ /* * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net> @@ -45,7 +45,6 @@ __FBSDID("$FreeBSD$"); #include <search.h> #include <signal.h> #include <stdarg.h> -#include <stdbool.h> #include <string.h> #include <unistd.h> #include <stdlib.h> @@ -53,7 +52,7 @@ __FBSDID("$FreeBSD$"); #include "extern.h" #include "pathnames.h" -#define BC_VER "1.0-FreeBSD" +#define BC_VER "1.1-FreeBSD" #define END_NODE ((ssize_t) -1) #define CONST_STRING ((ssize_t) -2) #define ALLOC_STRING ((ssize_t) -3) @@ -971,7 +970,12 @@ yyerror(const char *s) if (yyin != NULL && feof(yyin)) n = asprintf(&str, "%s: %s:%d: %s: unexpected EOF", __progname, filename, lineno, s); - else if (isspace(yytext[0]) || !isprint(yytext[0])) + else if (yytext[0] == '\n') + n = asprintf(&str, + "%s: %s:%d: %s: newline unexpected", + __progname, filename, lineno, s); + else if (isspace((unsigned char)yytext[0]) || + !isprint((unsigned char)yytext[0])) n = asprintf(&str, "%s: %s:%d: %s: ascii char 0x%02x unexpected", __progname, filename, lineno, s, yytext[0]); @@ -1085,26 +1089,25 @@ escape(const char *str) /* ARGSUSED */ static void -sigchld(int signo) +sigchld(int signo __unused) { pid_t pid; - int status; - - switch (signo) { - default: - for (;;) { - pid = waitpid(dc, &status, WUNTRACED); - if (pid == -1) { - if (errno == EINTR) - continue; - _exit(0); - } - if (WIFEXITED(status) || WIFSIGNALED(status)) - _exit(0); - else - break; - } + int status, save_errno = errno; + + for (;;) { + pid = waitpid(dc, &status, WCONTINUED | WNOHANG); + if (pid == -1) { + if (errno == EINTR) + continue; + _exit(0); + } else if (pid == 0) + break; + if (WIFEXITED(status) || WIFSIGNALED(status)) + _exit(0); + else + break; } + errno = save_errno; } static const char * @@ -1191,6 +1194,7 @@ main(int argc, char *argv[]) } } if (interactive) { + gettty(&ttysaved); el = el_init("bc", stdin, stderr, stderr); hist = history_init(); history(hist, &he, H_SETSIZE, 100); @@ -1198,6 +1202,8 @@ main(int argc, char *argv[]) el_set(el, EL_EDITOR, "emacs"); el_set(el, EL_SIGNAL, 1); el_set(el, EL_PROMPT, dummy_prompt); + el_set(el, EL_ADDFN, "bc_eof", "", bc_eof); + el_set(el, EL_BIND, "^D", "bc_eof", NULL); el_source(el, NULL); } yywrap(); diff --git a/usr.bin/bc/extern.h b/usr.bin/bc/extern.h index d1e9fe8..685942a 100644 --- a/usr.bin/bc/extern.h +++ b/usr.bin/bc/extern.h @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $OpenBSD: extern.h,v 1.6 2006/03/18 20:44:43 otto Exp $ */ +/* $OpenBSD: extern.h,v 1.10 2013/09/19 16:12:01 otto Exp $ */ /* * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net> @@ -17,26 +17,31 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <stdbool.h> #include <stdio.h> struct lvalue { - ssize_t load; - ssize_t store; + ssize_t load; + ssize_t store; }; -int yylex(void); -void yyerror(const char *); -void fatal(const char *); -void abort_line(int); +int yylex(void); +void yyerror(const char *); +void fatal(const char *); +void abort_line(int); +struct termios; +int gettty(struct termios *); +void tstpcont(int); +unsigned char bc_eof(EditLine *, int); -extern int lineno; -extern int fileindex; -extern int sargc; +extern int lineno; +extern int fileindex; +extern int sargc; extern const char **sargv; extern const char *filename; -extern char *cmdexpr; extern bool interactive; extern EditLine *el; extern History *hist; extern HistEvent he; - +extern char *cmdexpr; +extern struct termios ttysaved; diff --git a/usr.bin/bc/scan.l b/usr.bin/bc/scan.l index 71cb295..cb1977a 100644 --- a/usr.bin/bc/scan.l +++ b/usr.bin/bc/scan.l @@ -1,5 +1,5 @@ %{ -/* $OpenBSD: scan.l,v 1.23 2009/10/27 23:59:36 deraadt Exp $ */ +/* $OpenBSD: scan.l,v 1.28 2013/09/19 16:12:01 otto Exp $ */ /* * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net> @@ -24,6 +24,7 @@ __FBSDID("$FreeBSD$"); #include <errno.h> #include <histedit.h> #include <stdbool.h> +#include <signal.h> #include <string.h> #include <unistd.h> @@ -31,19 +32,22 @@ __FBSDID("$FreeBSD$"); #include "bc.h" #include "pathnames.h" -int lineno; +int lineno; +bool interactive; -bool interactive; HistEvent he; EditLine *el; History *hist; static char *strbuf = NULL; -static size_t strbuf_sz = 1; -static bool dot_seen; +static size_t strbuf_sz = 1; +static bool dot_seen; +static int use_el; +static volatile sig_atomic_t skipchars; + +static void init_strbuf(void); +static void add_str(const char *); -static void init_strbuf(void); -static void add_str(const char *); static int bc_yyinput(char *, int); #define YY_DECL int yylex(void) @@ -51,6 +55,7 @@ static int bc_yyinput(char *, int); #undef YY_INPUT #define YY_INPUT(buf,retval,max) \ (retval = bc_yyinput(buf, max)) + %} %option always-interactive @@ -102,7 +107,7 @@ ALPHANUM [a-z_0-9] BEGIN(INITIAL); yylval.str = strbuf; unput('.'); - return (NUMBER); + return NUMBER; } else { dot_seen = true; add_str("."); @@ -113,73 +118,73 @@ ALPHANUM [a-z_0-9] BEGIN(INITIAL); unput(yytext[0]); if (strcmp(strbuf, ".") == 0) - return (DOT); + return DOT; else { yylval.str = strbuf; - return (NUMBER); + return NUMBER; } } } -"auto" return (AUTO); -"break" return (BREAK); -"continue" return (CONTINUE); -"define" return (DEFINE); -"else" return (ELSE); -"ibase" return (IBASE); -"if" return (IF); -"last" return (DOT); -"for" return (FOR); -"length" return (LENGTH); -"obase" return (OBASE); -"print" return (PRINT); -"quit" return (QUIT); -"return" return (RETURN); -"scale" return (SCALE); -"sqrt" return (SQRT); -"while" return (WHILE); - -"^" return (EXPONENT); -"*" return (MULTIPLY); -"/" return (DIVIDE); -"%" return (REMAINDER); - -"!" return (BOOL_NOT); -"&&" return (BOOL_AND); -"||" return (BOOL_OR); - -"+" return (PLUS); -"-" return (MINUS); - -"++" return (INCR); -"--" return (DECR); - -"=" yylval.str = ""; return (ASSIGN_OP); -"+=" yylval.str = "+"; return (ASSIGN_OP); -"-=" yylval.str = "-"; return (ASSIGN_OP); -"*=" yylval.str = "*"; return (ASSIGN_OP); -"/=" yylval.str = "/"; return (ASSIGN_OP); -"%=" yylval.str = "%"; return (ASSIGN_OP); -"^=" yylval.str = "^"; return (ASSIGN_OP); - -"==" return (EQUALS); -"<=" return (LESS_EQ); -">=" return (GREATER_EQ); -"!=" return (UNEQUALS); -"<" return (LESS); -">" return (GREATER); - -"," return (COMMA); -";" return (SEMICOLON); - -"(" return (LPAR); -")" return (RPAR); - -"[" return (LBRACKET); -"]" return (RBRACKET); - -"{" return (LBRACE); -"}" return (RBRACE); +"auto" return AUTO; +"break" return BREAK; +"continue" return CONTINUE; +"define" return DEFINE; +"else" return ELSE; +"ibase" return IBASE; +"if" return IF; +"last" return DOT; +"for" return FOR; +"length" return LENGTH; +"obase" return OBASE; +"print" return PRINT; +"quit" return QUIT; +"return" return RETURN; +"scale" return SCALE; +"sqrt" return SQRT; +"while" return WHILE; + +"^" return EXPONENT; +"*" return MULTIPLY; +"/" return DIVIDE; +"%" return REMAINDER; + +"!" return BOOL_NOT; +"&&" return BOOL_AND; +"||" return BOOL_OR; + +"+" return PLUS; +"-" return MINUS; + +"++" return INCR; +"--" return DECR; + +"=" yylval.str = ""; return ASSIGN_OP; +"+=" yylval.str = "+"; return ASSIGN_OP; +"-=" yylval.str = "-"; return ASSIGN_OP; +"*=" yylval.str = "*"; return ASSIGN_OP; +"/=" yylval.str = "/"; return ASSIGN_OP; +"%=" yylval.str = "%"; return ASSIGN_OP; +"^=" yylval.str = "^"; return ASSIGN_OP; + +"==" return EQUALS; +"<=" return LESS_EQ; +">=" return GREATER_EQ; +"!=" return UNEQUALS; +"<" return LESS; +">" return GREATER; + +"," return COMMA; +";" return SEMICOLON; + +"(" return LPAR; +")" return RPAR; + +"[" return LBRACKET; +"]" return RBRACKET; + +"{" return LBRACE; +"}" return RBRACE; {ALPHA}{ALPHANUM}* { /* alloc an extra byte for the type marker */ @@ -188,15 +193,15 @@ ALPHANUM [a-z_0-9] err(1, NULL); strlcpy(p, yytext, yyleng + 1); yylval.astr = p; - return (LETTER); + return LETTER; } \\\n lineno++; -\n lineno++; return (NEWLINE); +\n lineno++; return NEWLINE; #[^\n]* ; [ \t] ; -<<EOF>> return (QUIT); +<<EOF>> return QUIT; . yyerror("illegal character"); %% @@ -204,7 +209,6 @@ ALPHANUM [a-z_0-9] static void init_strbuf(void) { - if (strbuf == NULL) { strbuf = malloc(strbuf_sz); if (strbuf == NULL) @@ -221,8 +225,8 @@ add_str(const char *str) arglen = strlen(str); if (strlen(strbuf) + arglen + 1 > strbuf_sz) { - size_t newsize; - char *p; + size_t newsize; + char *p; newsize = strbuf_sz + arglen + 1; p = realloc(strbuf, newsize); @@ -236,11 +240,47 @@ add_str(const char *str) strlcat(strbuf, str, strbuf_sz); } +/* ARGSUSED */ +void +abort_line(int sig __unused) +{ + static const char str1[] = "[\n]P\n"; + static const char str2[] = "[^C\n]P\n"; + int save_errno; + const LineInfo *info; + + save_errno = errno; + if (use_el) { + write(STDOUT_FILENO, str2, sizeof(str2) - 1); + info = el_line(el); + skipchars = info->lastchar - info->buffer; + } else + write(STDOUT_FILENO, str1, sizeof(str1) - 1); + errno = save_errno; +} + +/* + * Avoid the echo of ^D by the default code of editline and take + * into account skipchars to make ^D work when the cursor is at start of + * line after a ^C. + */ +unsigned char +bc_eof(EditLine *e, int ch __unused) +{ + const struct lineinfo *info = el_line(e); + + if (info->buffer + skipchars == info->cursor && + info->cursor == info->lastchar) + return (CC_EOF); + else + return (CC_ERROR); +} + int yywrap(void) { - static YY_BUFFER_STATE buf; static int state; + static YY_BUFFER_STATE buf; if (fileindex == 0 && sargc > 0 && strcmp(sargv[0], _PATH_LIBB) == 0) { filename = sargv[fileindex++]; @@ -273,6 +313,10 @@ yywrap(void) } else if (fileindex == sargc) { fileindex++; yyin = stdin; + if (interactive) { + signal(SIGINT, abort_line); + signal(SIGTSTP, tstpcont); + } lineno = 1; filename = "stdin"; return (0); @@ -284,17 +328,32 @@ static int bc_yyinput(char *buf, int maxlen) { int num; - if (yyin == stdin && interactive) { + + if (el != NULL) + el_get(el, EL_EDITMODE, &use_el); + + if (yyin == stdin && interactive && use_el) { const char *bp; + sigset_t oset, nset; if ((bp = el_gets(el, &num)) == NULL || num == 0) return (0); + sigemptyset(&nset); + sigaddset(&nset, SIGINT); + sigprocmask(SIG_BLOCK, &nset, &oset); + if (skipchars < num) { + bp += skipchars; + num -= skipchars; + } + skipchars = 0; + sigprocmask(SIG_SETMASK, &oset, NULL); if (num > maxlen) { - el_push(el, (char *)(uintptr_t)(bp) + maxlen); + el_push(el, bp + maxlen); num = maxlen; } memcpy(buf, bp, num); history(hist, &he, H_ENTER, bp); + el_get(el, EL_EDITMODE, &use_el); } else { int c = '*'; for (num = 0; num < maxlen && @@ -308,3 +367,4 @@ bc_yyinput(char *buf, int maxlen) return (num); } + diff --git a/usr.bin/bc/tty.c b/usr.bin/bc/tty.c new file mode 100644 index 0000000..05f9d14 --- /dev/null +++ b/usr.bin/bc/tty.c @@ -0,0 +1,65 @@ +/* $FreeBSD$ */ +/* $OpenBSD: tty.c,v 1.2 2013/11/12 13:54:51 deraadt Exp $ */ + +/* + * Copyright (c) 2013, Otto Moerbeek <otto@drijf.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <errno.h> +#include <signal.h> +#include <histedit.h> +#include <termios.h> +#include "extern.h" + +struct termios ttysaved, ttyedit; + +static int +settty(struct termios *t) +{ + int ret; + + while ((ret = tcsetattr(0, TCSADRAIN, t) == -1) && errno == EINTR) + continue; + return ret; +} + +int +gettty(struct termios *t) +{ + int ret; + + while ((ret = tcgetattr(0, t) == -1) && errno == EINTR) + continue; + return ret; +} + +/* ARGSUSED */ +void +tstpcont(int sig) +{ + int save_errno = errno; + + if (sig == SIGTSTP) { + signal(SIGCONT, tstpcont); + gettty(&ttyedit); + settty(&ttysaved); + } else { + signal(SIGTSTP, tstpcont); + settty(&ttyedit); + } + signal(sig, SIG_DFL); + kill(0, sig); + errno = save_errno; +} |