/* mpicalc.c - Simple RPN calculator using gcry_mpi functions
* Copyright (C) 1997, 1998, 1999, 2004, 2006, 2013 Werner Koch
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
*/
/*
This program is a simple RPN calculator which was originally used
to develop the mpi functions of GnuPG. Values must be given in
hex. Operation is like dc(1) except that the input/output radix is
always 16 and you can use a '-' to prefix a negative number.
Addition operators: ++ and --. All operators must be delimited by
a blank.
*/
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#ifdef _GCRYPT_IN_LIBGCRYPT
# undef _GCRYPT_IN_LIBGCRYPT
# include "gcrypt.h"
#else
# include
#endif
#define MPICALC_VERSION "2.0"
#define NEED_LIBGCRYPT_VERSION "1.6.0"
#define STACKSIZE 500
static gcry_mpi_t stack[STACKSIZE];
static int stackidx;
static int
scan_mpi (gcry_mpi_t retval, const char *string)
{
gpg_error_t err;
gcry_mpi_t val;
err = gcry_mpi_scan (&val, GCRYMPI_FMT_HEX, string, 0, NULL);
if (err)
{
fprintf (stderr, "scanning input failed: %s\n", gpg_strerror (err));
return -1;
}
mpi_set (retval, val);
mpi_release (val);
return 0;
}
static void
print_mpi (gcry_mpi_t a)
{
gpg_error_t err;
char *buf;
void *bufaddr = &buf;
err = gcry_mpi_aprint (GCRYMPI_FMT_HEX, bufaddr, NULL, a);
if (err)
fprintf (stderr, "[error printing number: %s]\n", gpg_strerror (err));
else
{
fputs (buf, stdout);
gcry_free (buf);
}
}
static void
do_add (void)
{
if (stackidx < 2)
{
fputs ("stack underflow\n", stderr);
return;
}
mpi_add (stack[stackidx - 2], stack[stackidx - 2], stack[stackidx - 1]);
stackidx--;
}
static void
do_sub (void)
{
if (stackidx < 2)
{
fputs ("stack underflow\n", stderr);
return;
}
mpi_sub (stack[stackidx - 2], stack[stackidx - 2], stack[stackidx - 1]);
stackidx--;
}
static void
do_inc (void)
{
if (stackidx < 1)
{
fputs ("stack underflow\n", stderr);
return;
}
mpi_add_ui (stack[stackidx - 1], stack[stackidx - 1], 1);
}
static void
do_dec (void)
{
if (stackidx < 1)
{
fputs ("stack underflow\n", stderr);
return;
}
/* mpi_sub_ui( stack[stackidx-1], stack[stackidx-1], 1 ); */
}
static void
do_mul (void)
{
if (stackidx < 2)
{
fputs ("stack underflow\n", stderr);
return;
}
mpi_mul (stack[stackidx - 2], stack[stackidx - 2], stack[stackidx - 1]);
stackidx--;
}
static void
do_mulm (void)
{
if (stackidx < 3)
{
fputs ("stack underflow\n", stderr);
return;
}
mpi_mulm (stack[stackidx - 3], stack[stackidx - 3],
stack[stackidx - 2], stack[stackidx - 1]);
stackidx -= 2;
}
static void
do_div (void)
{
if (stackidx < 2)
{
fputs ("stack underflow\n", stderr);
return;
}
mpi_fdiv (stack[stackidx - 2], NULL,
stack[stackidx - 2], stack[stackidx - 1]);
stackidx--;
}
static void
do_rem (void)
{
if (stackidx < 2)
{
fputs ("stack underflow\n", stderr);
return;
}
mpi_mod (stack[stackidx - 2],
stack[stackidx - 2], stack[stackidx - 1]);
stackidx--;
}
static void
do_powm (void)
{
gcry_mpi_t a;
if (stackidx < 3)
{
fputs ("stack underflow\n", stderr);
return;
}
a = mpi_new (0);
mpi_powm (a, stack[stackidx - 3], stack[stackidx - 2], stack[stackidx - 1]);
mpi_release (stack[stackidx - 3]);
stack[stackidx - 3] = a;
stackidx -= 2;
}
static void
do_inv (void)
{
gcry_mpi_t a = mpi_new (0);
if (stackidx < 2)
{
fputs ("stack underflow\n", stderr);
return;
}
mpi_invm (a, stack[stackidx - 2], stack[stackidx - 1]);
mpi_set (stack[stackidx - 2], a);
mpi_release (a);
stackidx--;
}
static void
do_gcd (void)
{
gcry_mpi_t a = mpi_new (0);
if (stackidx < 2)
{
fputs ("stack underflow\n", stderr);
return;
}
mpi_gcd (a, stack[stackidx - 2], stack[stackidx - 1]);
mpi_set (stack[stackidx - 2], a);
mpi_release (a);
stackidx--;
}
static void
do_rshift (void)
{
if (stackidx < 1)
{
fputs ("stack underflow\n", stderr);
return;
}
mpi_rshift (stack[stackidx - 1], stack[stackidx - 1], 1);
}
static void
do_nbits (void)
{
unsigned int n;
if (stackidx < 1)
{
fputs ("stack underflow\n", stderr);
return;
}
n = mpi_get_nbits (stack[stackidx - 1]);
mpi_set_ui (stack[stackidx - 1], n);
}
static void
do_primecheck (void)
{
gpg_error_t err;
if (stackidx < 1)
{
fputs ("stack underflow\n", stderr);
return;
}
err = gcry_prime_check (stack[stackidx - 1], 0);
mpi_set_ui (stack[stackidx - 1], !err);
if (err && gpg_err_code (err) != GPG_ERR_NO_PRIME)
fprintf (stderr, "checking prime failed: %s\n", gpg_strerror (err));
}
static int
my_getc (void)
{
static int shown;
int c;
for (;;)
{
if ((c = getc (stdin)) == EOF)
return EOF;
if (!(c & 0x80))
return c;
if (!shown)
{
shown = 1;
fputs ("note: Non ASCII characters are ignored\n", stderr);
}
}
}
static void
print_help (void)
{
fputs ("+ add [0] := [1] + [0] {-1}\n"
"- subtract [0] := [1] - [0] {-1}\n"
"* multiply [0] := [1] * [0] {-1}\n"
"/ divide [0] := [1] - [0] {-1}\n"
"% modulo [0] := [1] % [0] {-1}\n"
"> right shift [0] := [0] >> 1 {0}\n"
"++ increment [0] := [0]++ {0}\n"
"-- decrement [0] := [0]-- {0}\n"
"m multiply mod [0] := [2] * [1] mod [0] {-2}\n"
"^ power mod [0] := [2] ^ [1] mod [0] {-2}\n"
"I inverse mod [0] := [1]^-1 mod [0] {-1}\n"
"G gcd [0] := gcd([1],[0]) {-1}\n"
"i remove item [0] := [1] {-1}\n"
"d dup item [-1] := [0] {+1}\n"
"r reverse [0] := [1], [1] := [0] {0}\n"
"b # of bits [0] := nbits([0]) {0}\n"
"P prime check [0] := is_prime([0])?1:0 {0}\n"
"c clear stack\n"
"p print top item\n"
"f print the stack\n"
"# ignore until end of line\n"
"? print this help\n"
, stdout);
}
int
main (int argc, char **argv)
{
const char *pgm;
int last_argc = -1;
int print_config = 0;
int i, c;
int state = 0;
char strbuf[4096];
int stridx = 0;
if (argc)
{
pgm = strrchr (*argv, '/');
if (pgm)
pgm++;
else
pgm = *argv;
argc--; argv++;
}
else
pgm = "?";
while (argc && last_argc != argc )
{
last_argc = argc;
if (!strcmp (*argv, "--"))
{
argc--; argv++;
break;
}
else if (!strcmp (*argv, "--version")
|| !strcmp (*argv, "--help"))
{
printf ("%s " MPICALC_VERSION "\n"
"libgcrypt %s\n"
"Copyright (C) 1997, 2013 Werner Koch\n"
"License LGPLv2.1+: GNU LGPL version 2.1 or later "
"\n"
"This is free software: you are free to change and "
"redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n"
"\n"
"Syntax: mpicalc [options]\n"
"Simple interactive big integer RPN calculator\n"
"\n"
"Options:\n"
" --version print version information\n"
" --print-config print the Libgcrypt config\n"
" --disable-hwf NAME disable feature NAME\n",
pgm, gcry_check_version (NULL));
exit (0);
}
else if (!strcmp (*argv, "--print-config"))
{
argc--; argv++;
print_config = 1;
}
else if (!strcmp (*argv, "--disable-hwf"))
{
argc--; argv++;
if (argc)
{
if (gcry_control (GCRYCTL_DISABLE_HWF, *argv, NULL))
fprintf (stderr, "%s: unknown hardware feature `%s'"
" - option ignored\n", pgm, *argv);
argc--; argv++;
}
}
}
if (argc)
{
fprintf (stderr, "usage: %s [options] (--help for help)\n", pgm);
exit (1);
}
if (!gcry_check_version (NEED_LIBGCRYPT_VERSION))
{
fprintf (stderr, "%s: Libgcrypt is too old (need %s, have %s)\n",
pgm, NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
exit (1);
}
gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
if (print_config)
{
gcry_control (GCRYCTL_PRINT_CONFIG, stdout);
exit (0);
}
for (i = 0; i < STACKSIZE; i++)
stack[i] = NULL;
stackidx = 0;
while ((c = my_getc ()) != EOF)
{
if (!state) /* waiting */
{
if (isdigit (c))
{
state = 1;
ungetc (c, stdin);
strbuf[0] = '0';
strbuf[1] = 'x';
stridx = 2;
}
else if (isspace (c))
;
else
{
switch (c)
{
case '#':
state = 2;
break;
case '+':
if ((c = my_getc ()) == '+')
do_inc ();
else
{
ungetc (c, stdin);
do_add ();
}
break;
case '-':
if ((c = my_getc ()) == '-')
do_dec ();
else if (isdigit (c)
|| (c >= 'A' && c <= 'F')
|| (c >= 'a' && c <= 'f'))
{
state = 1;
ungetc (c, stdin);
strbuf[0] = '-';
strbuf[1] = '0';
strbuf[2] = 'x';
stridx = 3;
}
else
{
ungetc (c, stdin);
do_sub ();
}
break;
case '*':
do_mul ();
break;
case 'm':
do_mulm ();
break;
case '/':
do_div ();
break;
case '%':
do_rem ();
break;
case '^':
do_powm ();
break;
case '>':
do_rshift ();
break;
case 'I':
do_inv ();
break;
case 'G':
do_gcd ();
break;
case 'i': /* dummy */
if (!stackidx)
fputs ("stack underflow\n", stderr);
else
{
mpi_release (stack[stackidx - 1]);
stackidx--;
}
break;
case 'd': /* duplicate the tos */
if (!stackidx)
fputs ("stack underflow\n", stderr);
else if (stackidx < STACKSIZE)
{
mpi_release (stack[stackidx]);
stack[stackidx] = mpi_copy (stack[stackidx - 1]);
stackidx++;
}
else
fputs ("stack overflow\n", stderr);
break;
case 'r': /* swap top elements */
if (stackidx < 2)
fputs ("stack underflow\n", stderr);
else if (stackidx < STACKSIZE)
{
gcry_mpi_t tmp = stack[stackidx-1];
stack[stackidx-1] = stack[stackidx - 2];
stack[stackidx-2] = tmp;
}
break;
case 'b':
do_nbits ();
break;
case 'P':
do_primecheck ();
break;
case 'c':
for (i = 0; i < stackidx; i++)
{
mpi_release (stack[i]); stack[i] = NULL;
}
stackidx = 0;
break;
case 'p': /* print the tos */
if (!stackidx)
puts ("stack is empty");
else
{
print_mpi (stack[stackidx - 1]);
putchar ('\n');
}
break;
case 'f': /* print the stack */
for (i = stackidx - 1; i >= 0; i--)
{
printf ("[%2d]: ", i);
print_mpi (stack[i]);
putchar ('\n');
}
break;
case '?':
print_help ();
break;
default:
fputs ("invalid operator\n", stderr);
}
}
}
else if (state == 1) /* In a number. */
{
if (!isxdigit (c))
{
/* Store the number */
state = 0;
ungetc (c, stdin);
if (stridx < sizeof strbuf)
strbuf[stridx] = 0;
if (stackidx < STACKSIZE)
{
if (!stack[stackidx])
stack[stackidx] = mpi_new (0);
if (scan_mpi (stack[stackidx], strbuf))
fputs ("invalid number\n", stderr);
else
stackidx++;
}
else
fputs ("stack overflow\n", stderr);
}
else
{ /* Store a digit. */
if (stridx < sizeof strbuf - 1)
strbuf[stridx++] = c;
else if (stridx == sizeof strbuf - 1)
{
strbuf[stridx] = 0;
fputs ("input too large - truncated\n", stderr);
stridx++;
}
}
}
else if (state == 2) /* In a comment. */
{
if (c == '\n')
state = 0;
}
}
for (i = 0; i < stackidx; i++)
mpi_release (stack[i]);
return 0;
}