Line data Source code
1 : /* mpicalc.c - Simple RPN calculator using gcry_mpi functions
2 : * Copyright (C) 1997, 1998, 1999, 2004, 2006, 2013 Werner Koch
3 : *
4 : * This program is free software; you can redistribute it and/or modify
5 : * it under the terms of the GNU Lesser General Public License as
6 : * published by the Free Software Foundation; either version 2.1 of
7 : * the License, or (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU Lesser General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU Lesser General Public
15 : * License along with this program; if not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : /*
19 : This program is a simple RPN calculator which was originally used
20 : to develop the mpi functions of GnuPG. Values must be given in
21 : hex. Operation is like dc(1) except that the input/output radix is
22 : always 16 and you can use a '-' to prefix a negative number.
23 : Addition operators: ++ and --. All operators must be delimited by
24 : a blank.
25 : */
26 :
27 : #ifdef HAVE_CONFIG_H
28 : # include <config.h>
29 : #endif
30 : #include <stdio.h>
31 : #include <stdlib.h>
32 : #include <ctype.h>
33 :
34 : #ifdef _GCRYPT_IN_LIBGCRYPT
35 : # undef _GCRYPT_IN_LIBGCRYPT
36 : # include "gcrypt.h"
37 : #else
38 : # include <gcrypt.h>
39 : #endif
40 :
41 :
42 : #define MPICALC_VERSION "2.0"
43 : #define NEED_LIBGCRYPT_VERSION "1.6.0"
44 :
45 : #define STACKSIZE 500
46 : static gcry_mpi_t stack[STACKSIZE];
47 : static int stackidx;
48 :
49 :
50 : static int
51 0 : scan_mpi (gcry_mpi_t retval, const char *string)
52 : {
53 : gpg_error_t err;
54 : gcry_mpi_t val;
55 :
56 0 : err = gcry_mpi_scan (&val, GCRYMPI_FMT_HEX, string, 0, NULL);
57 0 : if (err)
58 : {
59 0 : fprintf (stderr, "scanning input failed: %s\n", gpg_strerror (err));
60 0 : return -1;
61 : }
62 0 : mpi_set (retval, val);
63 0 : mpi_release (val);
64 0 : return 0;
65 : }
66 :
67 :
68 : static void
69 0 : print_mpi (gcry_mpi_t a)
70 : {
71 : gpg_error_t err;
72 : char *buf;
73 0 : void *bufaddr = &buf;
74 :
75 0 : err = gcry_mpi_aprint (GCRYMPI_FMT_HEX, bufaddr, NULL, a);
76 0 : if (err)
77 0 : fprintf (stderr, "[error printing number: %s]\n", gpg_strerror (err));
78 : else
79 : {
80 0 : fputs (buf, stdout);
81 0 : gcry_free (buf);
82 : }
83 0 : }
84 :
85 :
86 :
87 : static void
88 0 : do_add (void)
89 : {
90 0 : if (stackidx < 2)
91 : {
92 0 : fputs ("stack underflow\n", stderr);
93 0 : return;
94 : }
95 0 : mpi_add (stack[stackidx - 2], stack[stackidx - 2], stack[stackidx - 1]);
96 0 : stackidx--;
97 : }
98 :
99 : static void
100 0 : do_sub (void)
101 : {
102 0 : if (stackidx < 2)
103 : {
104 0 : fputs ("stack underflow\n", stderr);
105 0 : return;
106 : }
107 0 : mpi_sub (stack[stackidx - 2], stack[stackidx - 2], stack[stackidx - 1]);
108 0 : stackidx--;
109 : }
110 :
111 : static void
112 0 : do_inc (void)
113 : {
114 0 : if (stackidx < 1)
115 : {
116 0 : fputs ("stack underflow\n", stderr);
117 0 : return;
118 : }
119 0 : mpi_add_ui (stack[stackidx - 1], stack[stackidx - 1], 1);
120 : }
121 :
122 : static void
123 0 : do_dec (void)
124 : {
125 0 : if (stackidx < 1)
126 : {
127 0 : fputs ("stack underflow\n", stderr);
128 0 : return;
129 : }
130 : /* mpi_sub_ui( stack[stackidx-1], stack[stackidx-1], 1 ); */
131 : }
132 :
133 : static void
134 0 : do_mul (void)
135 : {
136 0 : if (stackidx < 2)
137 : {
138 0 : fputs ("stack underflow\n", stderr);
139 0 : return;
140 : }
141 0 : mpi_mul (stack[stackidx - 2], stack[stackidx - 2], stack[stackidx - 1]);
142 0 : stackidx--;
143 : }
144 :
145 : static void
146 0 : do_mulm (void)
147 : {
148 0 : if (stackidx < 3)
149 : {
150 0 : fputs ("stack underflow\n", stderr);
151 0 : return;
152 : }
153 0 : mpi_mulm (stack[stackidx - 3], stack[stackidx - 3],
154 : stack[stackidx - 2], stack[stackidx - 1]);
155 0 : stackidx -= 2;
156 : }
157 :
158 : static void
159 0 : do_div (void)
160 : {
161 0 : if (stackidx < 2)
162 : {
163 0 : fputs ("stack underflow\n", stderr);
164 0 : return;
165 : }
166 0 : mpi_fdiv (stack[stackidx - 2], NULL,
167 : stack[stackidx - 2], stack[stackidx - 1]);
168 0 : stackidx--;
169 : }
170 :
171 : static void
172 0 : do_rem (void)
173 : {
174 0 : if (stackidx < 2)
175 : {
176 0 : fputs ("stack underflow\n", stderr);
177 0 : return;
178 : }
179 0 : mpi_mod (stack[stackidx - 2],
180 : stack[stackidx - 2], stack[stackidx - 1]);
181 0 : stackidx--;
182 : }
183 :
184 : static void
185 0 : do_powm (void)
186 : {
187 : gcry_mpi_t a;
188 0 : if (stackidx < 3)
189 : {
190 0 : fputs ("stack underflow\n", stderr);
191 0 : return;
192 : }
193 0 : a = mpi_new (0);
194 0 : mpi_powm (a, stack[stackidx - 3], stack[stackidx - 2], stack[stackidx - 1]);
195 0 : mpi_release (stack[stackidx - 3]);
196 0 : stack[stackidx - 3] = a;
197 0 : stackidx -= 2;
198 : }
199 :
200 : static void
201 0 : do_inv (void)
202 : {
203 : gcry_mpi_t a;
204 :
205 0 : if (stackidx < 2)
206 : {
207 0 : fputs ("stack underflow\n", stderr);
208 0 : return;
209 : }
210 0 : a = mpi_new (0);
211 0 : mpi_invm (a, stack[stackidx - 2], stack[stackidx - 1]);
212 0 : mpi_set (stack[stackidx - 2], a);
213 0 : mpi_release (a);
214 0 : stackidx--;
215 : }
216 :
217 : static void
218 0 : do_gcd (void)
219 : {
220 : gcry_mpi_t a;
221 :
222 0 : if (stackidx < 2)
223 : {
224 0 : fputs ("stack underflow\n", stderr);
225 0 : return;
226 : }
227 0 : a = mpi_new (0);
228 0 : mpi_gcd (a, stack[stackidx - 2], stack[stackidx - 1]);
229 0 : mpi_set (stack[stackidx - 2], a);
230 0 : mpi_release (a);
231 0 : stackidx--;
232 : }
233 :
234 : static void
235 0 : do_rshift (void)
236 : {
237 0 : if (stackidx < 1)
238 : {
239 0 : fputs ("stack underflow\n", stderr);
240 0 : return;
241 : }
242 0 : mpi_rshift (stack[stackidx - 1], stack[stackidx - 1], 1);
243 : }
244 :
245 :
246 : static void
247 0 : do_nbits (void)
248 : {
249 : unsigned int n;
250 :
251 0 : if (stackidx < 1)
252 : {
253 0 : fputs ("stack underflow\n", stderr);
254 0 : return;
255 : }
256 0 : n = mpi_get_nbits (stack[stackidx - 1]);
257 0 : mpi_set_ui (stack[stackidx - 1], n);
258 : }
259 :
260 :
261 : static void
262 0 : do_primecheck (void)
263 : {
264 : gpg_error_t err;
265 :
266 0 : if (stackidx < 1)
267 : {
268 0 : fputs ("stack underflow\n", stderr);
269 0 : return;
270 : }
271 0 : err = gcry_prime_check (stack[stackidx - 1], 0);
272 0 : mpi_set_ui (stack[stackidx - 1], !err);
273 0 : if (err && gpg_err_code (err) != GPG_ERR_NO_PRIME)
274 0 : fprintf (stderr, "checking prime failed: %s\n", gpg_strerror (err));
275 : }
276 :
277 :
278 : static int
279 0 : my_getc (void)
280 : {
281 : static int shown;
282 : int c;
283 :
284 : for (;;)
285 : {
286 0 : if ((c = getc (stdin)) == EOF)
287 0 : return EOF;
288 0 : if (!(c & 0x80))
289 0 : return c;
290 :
291 0 : if (!shown)
292 : {
293 0 : shown = 1;
294 0 : fputs ("note: Non ASCII characters are ignored\n", stderr);
295 : }
296 : }
297 : }
298 :
299 :
300 : static void
301 0 : print_help (void)
302 : {
303 0 : fputs ("+ add [0] := [1] + [0] {-1}\n"
304 : "- subtract [0] := [1] - [0] {-1}\n"
305 : "* multiply [0] := [1] * [0] {-1}\n"
306 : "/ divide [0] := [1] - [0] {-1}\n"
307 : "% modulo [0] := [1] % [0] {-1}\n"
308 : "> right shift [0] := [0] >> 1 {0}\n"
309 : "++ increment [0] := [0]++ {0}\n"
310 : "-- decrement [0] := [0]-- {0}\n"
311 : "m multiply mod [0] := [2] * [1] mod [0] {-2}\n"
312 : "^ power mod [0] := [2] ^ [1] mod [0] {-2}\n"
313 : "I inverse mod [0] := [1]^-1 mod [0] {-1}\n"
314 : "G gcd [0] := gcd([1],[0]) {-1}\n"
315 : "i remove item [0] := [1] {-1}\n"
316 : "d dup item [-1] := [0] {+1}\n"
317 : "r reverse [0] := [1], [1] := [0] {0}\n"
318 : "b # of bits [0] := nbits([0]) {0}\n"
319 : "P prime check [0] := is_prime([0])?1:0 {0}\n"
320 : "c clear stack\n"
321 : "p print top item\n"
322 : "f print the stack\n"
323 : "# ignore until end of line\n"
324 : "? print this help\n"
325 : , stdout);
326 0 : }
327 :
328 :
329 :
330 : int
331 0 : main (int argc, char **argv)
332 : {
333 : const char *pgm;
334 0 : int last_argc = -1;
335 0 : int print_config = 0;
336 : int i, c;
337 0 : int state = 0;
338 : char strbuf[4096];
339 0 : int stridx = 0;
340 :
341 0 : if (argc)
342 : {
343 0 : pgm = strrchr (*argv, '/');
344 0 : if (pgm)
345 0 : pgm++;
346 : else
347 0 : pgm = *argv;
348 0 : argc--; argv++;
349 : }
350 : else
351 0 : pgm = "?";
352 :
353 0 : while (argc && last_argc != argc )
354 : {
355 0 : last_argc = argc;
356 0 : if (!strcmp (*argv, "--"))
357 : {
358 0 : argc--; argv++;
359 0 : break;
360 : }
361 0 : else if (!strcmp (*argv, "--version")
362 0 : || !strcmp (*argv, "--help"))
363 : {
364 0 : printf ("%s " MPICALC_VERSION "\n"
365 : "libgcrypt %s\n"
366 : "Copyright (C) 1997, 2013 Werner Koch\n"
367 : "License LGPLv2.1+: GNU LGPL version 2.1 or later "
368 : "<http://gnu.org/licenses/old-licenses/lgpl-2.1.html>\n"
369 : "This is free software: you are free to change and "
370 : "redistribute it.\n"
371 : "There is NO WARRANTY, to the extent permitted by law.\n"
372 : "\n"
373 : "Syntax: mpicalc [options]\n"
374 : "Simple interactive big integer RPN calculator\n"
375 : "\n"
376 : "Options:\n"
377 : " --version print version information\n"
378 : " --print-config print the Libgcrypt config\n"
379 : " --disable-hwf NAME disable feature NAME\n",
380 : pgm, gcry_check_version (NULL));
381 0 : exit (0);
382 : }
383 0 : else if (!strcmp (*argv, "--print-config"))
384 : {
385 0 : argc--; argv++;
386 0 : print_config = 1;
387 : }
388 0 : else if (!strcmp (*argv, "--disable-hwf"))
389 : {
390 0 : argc--; argv++;
391 0 : if (argc)
392 : {
393 0 : if (gcry_control (GCRYCTL_DISABLE_HWF, *argv, NULL))
394 0 : fprintf (stderr, "%s: unknown hardware feature `%s'"
395 : " - option ignored\n", pgm, *argv);
396 0 : argc--; argv++;
397 : }
398 : }
399 : }
400 :
401 0 : if (argc)
402 : {
403 0 : fprintf (stderr, "usage: %s [options] (--help for help)\n", pgm);
404 0 : exit (1);
405 : }
406 :
407 0 : if (!gcry_check_version (NEED_LIBGCRYPT_VERSION))
408 : {
409 0 : fprintf (stderr, "%s: Libgcrypt is too old (need %s, have %s)\n",
410 : pgm, NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
411 0 : exit (1);
412 : }
413 0 : gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
414 0 : gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
415 0 : if (print_config)
416 : {
417 0 : gcry_control (GCRYCTL_PRINT_CONFIG, stdout);
418 0 : exit (0);
419 : }
420 :
421 0 : for (i = 0; i < STACKSIZE; i++)
422 0 : stack[i] = NULL;
423 0 : stackidx = 0;
424 :
425 0 : while ((c = my_getc ()) != EOF)
426 : {
427 0 : if (!state) /* waiting */
428 : {
429 0 : if (isdigit (c))
430 : {
431 0 : state = 1;
432 0 : ungetc (c, stdin);
433 0 : strbuf[0] = '0';
434 0 : strbuf[1] = 'x';
435 0 : stridx = 2;
436 : }
437 0 : else if (isspace (c))
438 : ;
439 : else
440 : {
441 0 : switch (c)
442 : {
443 : case '#':
444 0 : state = 2;
445 0 : break;
446 : case '+':
447 0 : if ((c = my_getc ()) == '+')
448 0 : do_inc ();
449 : else
450 : {
451 0 : ungetc (c, stdin);
452 0 : do_add ();
453 : }
454 0 : break;
455 : case '-':
456 0 : if ((c = my_getc ()) == '-')
457 0 : do_dec ();
458 0 : else if (isdigit (c)
459 0 : || (c >= 'A' && c <= 'F')
460 0 : || (c >= 'a' && c <= 'f'))
461 : {
462 0 : state = 1;
463 0 : ungetc (c, stdin);
464 0 : strbuf[0] = '-';
465 0 : strbuf[1] = '0';
466 0 : strbuf[2] = 'x';
467 0 : stridx = 3;
468 : }
469 : else
470 : {
471 0 : ungetc (c, stdin);
472 0 : do_sub ();
473 : }
474 0 : break;
475 : case '*':
476 0 : do_mul ();
477 0 : break;
478 : case 'm':
479 0 : do_mulm ();
480 0 : break;
481 : case '/':
482 0 : do_div ();
483 0 : break;
484 : case '%':
485 0 : do_rem ();
486 0 : break;
487 : case '^':
488 0 : do_powm ();
489 0 : break;
490 : case '>':
491 0 : do_rshift ();
492 0 : break;
493 : case 'I':
494 0 : do_inv ();
495 0 : break;
496 : case 'G':
497 0 : do_gcd ();
498 0 : break;
499 : case 'i': /* dummy */
500 0 : if (!stackidx)
501 0 : fputs ("stack underflow\n", stderr);
502 : else
503 : {
504 0 : mpi_release (stack[stackidx - 1]);
505 0 : stackidx--;
506 : }
507 0 : break;
508 : case 'd': /* duplicate the tos */
509 0 : if (!stackidx)
510 0 : fputs ("stack underflow\n", stderr);
511 0 : else if (stackidx < STACKSIZE)
512 : {
513 0 : mpi_release (stack[stackidx]);
514 0 : stack[stackidx] = mpi_copy (stack[stackidx - 1]);
515 0 : stackidx++;
516 : }
517 : else
518 0 : fputs ("stack overflow\n", stderr);
519 0 : break;
520 : case 'r': /* swap top elements */
521 0 : if (stackidx < 2)
522 0 : fputs ("stack underflow\n", stderr);
523 0 : else if (stackidx < STACKSIZE)
524 : {
525 0 : gcry_mpi_t tmp = stack[stackidx-1];
526 0 : stack[stackidx-1] = stack[stackidx - 2];
527 0 : stack[stackidx-2] = tmp;
528 : }
529 0 : break;
530 : case 'b':
531 0 : do_nbits ();
532 0 : break;
533 : case 'P':
534 0 : do_primecheck ();
535 0 : break;
536 : case 'c':
537 0 : for (i = 0; i < stackidx; i++)
538 : {
539 0 : mpi_release (stack[i]); stack[i] = NULL;
540 : }
541 0 : stackidx = 0;
542 0 : break;
543 : case 'p': /* print the tos */
544 0 : if (!stackidx)
545 0 : puts ("stack is empty");
546 : else
547 : {
548 0 : print_mpi (stack[stackidx - 1]);
549 0 : putchar ('\n');
550 : }
551 0 : break;
552 : case 'f': /* print the stack */
553 0 : for (i = stackidx - 1; i >= 0; i--)
554 : {
555 0 : printf ("[%2d]: ", i);
556 0 : print_mpi (stack[i]);
557 0 : putchar ('\n');
558 : }
559 0 : break;
560 : case '?':
561 0 : print_help ();
562 0 : break;
563 : default:
564 0 : fputs ("invalid operator\n", stderr);
565 : }
566 : }
567 : }
568 0 : else if (state == 1) /* In a number. */
569 : {
570 0 : if (!isxdigit (c))
571 : {
572 : /* Store the number */
573 0 : state = 0;
574 0 : ungetc (c, stdin);
575 0 : if (stridx < sizeof strbuf)
576 0 : strbuf[stridx] = 0;
577 :
578 0 : if (stackidx < STACKSIZE)
579 : {
580 0 : if (!stack[stackidx])
581 0 : stack[stackidx] = mpi_new (0);
582 0 : if (scan_mpi (stack[stackidx], strbuf))
583 0 : fputs ("invalid number\n", stderr);
584 : else
585 0 : stackidx++;
586 : }
587 : else
588 0 : fputs ("stack overflow\n", stderr);
589 : }
590 : else
591 : { /* Store a digit. */
592 0 : if (stridx < sizeof strbuf - 1)
593 0 : strbuf[stridx++] = c;
594 0 : else if (stridx == sizeof strbuf - 1)
595 : {
596 0 : strbuf[stridx] = 0;
597 0 : fputs ("input too large - truncated\n", stderr);
598 0 : stridx++;
599 : }
600 : }
601 : }
602 0 : else if (state == 2) /* In a comment. */
603 : {
604 0 : if (c == '\n')
605 0 : state = 0;
606 : }
607 :
608 : }
609 :
610 0 : for (i = 0; i < stackidx; i++)
611 0 : mpi_release (stack[i]);
612 0 : return 0;
613 : }
|