| /* 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 <http://www.gnu.org/licenses/>. |
| */ |
| |
| /* |
| 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 <config.h> |
| #endif |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <ctype.h> |
| |
| #ifdef _GCRYPT_IN_LIBGCRYPT |
| # undef _GCRYPT_IN_LIBGCRYPT |
| # include "gcrypt.h" |
| #else |
| # include <gcrypt.h> |
| #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 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" |
| "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[1000]; |
| 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 " |
| "<http://gnu.org/licenses/old-licenses/lgpl-2.1.html>\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 '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; |
| } |