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