Replace ath based mutexes by gpgrt based locks.
[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 int
258 my_getc (void)
259 {
260   static int shown;
261   int c;
262
263   for (;;)
264     {
265       if ((c = getc (stdin)) == EOF)
266         return EOF;
267       if (!(c & 0x80))
268         return c;
269
270       if (!shown)
271         {
272           shown = 1;
273           fputs ("note: Non ASCII characters are ignored\n", stderr);
274         }
275     }
276 }
277
278
279 static void
280 print_help (void)
281 {
282   fputs ("+   add           [0] := [1] + [0]          {-1}\n"
283          "-   subtract      [0] := [1] - [0]          {-1}\n"
284          "*   multiply      [0] := [1] * [0]          {-1}\n"
285          "/   divide        [0] := [1] - [0]          {-1}\n"
286          "%   modulo        [0] := [1] % [0]          {-1}\n"
287          ">   right shift   [0] := [0] >> 1           {0}\n"
288          "++  increment     [0] := [0]++              {0}\n"
289          "--  decrement     [0] := [0]--              {0}\n"
290          "m   multiply mod  [0] := [2] * [1] mod [0]  {-2}\n"
291          "^   power mod     [0] := [2] ^ [1] mod [0]  {-2}\n"
292          "I   inverse mod   [0] := [1]^-1 mod [0]     {-1}\n"
293          "G   gcd           [0] := gcd([1],[0])       {-1}\n"
294          "i   remove item   [0] := [1]                {-1}\n"
295          "d   dup item      [-1] := [0]               {+1}\n"
296          "r   reverse       [0] := [1], [1] := [0]    {0}\n"
297          "b   # of bits     [0] := nbits([0])         {0}\n"
298          "c   clear stack\n"
299          "p   print top item\n"
300          "f   print the stack\n"
301          "#   ignore until end of line\n"
302          "?   print this help\n"
303          , stdout);
304 }
305
306
307
308 int
309 main (int argc, char **argv)
310 {
311   const char *pgm;
312   int last_argc = -1;
313   int print_config = 0;
314   int i, c;
315   int state = 0;
316   char strbuf[1000];
317   int stridx = 0;
318
319   if (argc)
320     {
321       pgm = strrchr (*argv, '/');
322       if (pgm)
323         pgm++;
324       else
325         pgm = *argv;
326       argc--; argv++;
327     }
328   else
329     pgm = "?";
330
331   while (argc && last_argc != argc )
332     {
333       last_argc = argc;
334       if (!strcmp (*argv, "--"))
335         {
336           argc--; argv++;
337           break;
338         }
339       else if (!strcmp (*argv, "--version")
340                || !strcmp (*argv, "--help"))
341         {
342           printf ("%s " MPICALC_VERSION "\n"
343                   "libgcrypt %s\n"
344                   "Copyright (C) 1997, 2013  Werner Koch\n"
345                   "License LGPLv2.1+: GNU LGPL version 2.1 or later "
346                   "<http://gnu.org/licenses/old-licenses/lgpl-2.1.html>\n"
347                   "This is free software: you are free to change and "
348                   "redistribute it.\n"
349                   "There is NO WARRANTY, to the extent permitted by law.\n"
350                   "\n"
351                   "Syntax: mpicalc [options]\n"
352                   "Simple interactive big integer RPN calculator\n"
353                   "\n"
354                   "Options:\n"
355                   "  --version           print version information\n"
356                   "  --print-config      print the Libgcrypt config\n"
357                   "  --disable-hwf NAME  disable feature NAME\n",
358                   pgm, gcry_check_version (NULL));
359           exit (0);
360         }
361       else if (!strcmp (*argv, "--print-config"))
362         {
363           argc--; argv++;
364           print_config = 1;
365         }
366       else if (!strcmp (*argv, "--disable-hwf"))
367         {
368           argc--; argv++;
369           if (argc)
370             {
371               if (gcry_control (GCRYCTL_DISABLE_HWF, *argv, NULL))
372                 fprintf (stderr, "%s: unknown hardware feature `%s'"
373                          " - option ignored\n", pgm, *argv);
374               argc--; argv++;
375             }
376         }
377     }
378
379   if (argc)
380     {
381       fprintf (stderr, "usage: %s [options]  (--help for help)\n", pgm);
382       exit (1);
383     }
384
385   if (!gcry_check_version (NEED_LIBGCRYPT_VERSION))
386     {
387       fprintf (stderr, "%s: Libgcrypt is too old (need %s, have %s)\n",
388                pgm, NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
389       exit (1);
390     }
391   gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
392   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
393   if (print_config)
394     {
395       gcry_control (GCRYCTL_PRINT_CONFIG, stdout);
396       exit (0);
397     }
398
399   for (i = 0; i < STACKSIZE; i++)
400     stack[i] = NULL;
401   stackidx = 0;
402
403   while ((c = my_getc ()) != EOF)
404     {
405       if (!state) /* waiting */
406         {
407           if (isdigit (c))
408             {
409               state = 1;
410               ungetc (c, stdin);
411               strbuf[0] = '0';
412               strbuf[1] = 'x';
413               stridx = 2;
414             }
415           else if (isspace (c))
416             ;
417           else
418             {
419               switch (c)
420                 {
421                 case '#':
422                   state = 2;
423                   break;
424                 case '+':
425                   if ((c = my_getc ()) == '+')
426                     do_inc ();
427                   else
428                     {
429                       ungetc (c, stdin);
430                       do_add ();
431                     }
432                   break;
433                 case '-':
434                   if ((c = my_getc ()) == '-')
435                     do_dec ();
436                   else if (isdigit (c)
437                            || (c >= 'A' && c <= 'F')
438                            || (c >= 'a' && c <= 'f'))
439                     {
440                       state = 1;
441                       ungetc (c, stdin);
442                       strbuf[0] = '-';
443                       strbuf[1] = '0';
444                       strbuf[2] = 'x';
445                       stridx = 3;
446                     }
447                   else
448                     {
449                       ungetc (c, stdin);
450                       do_sub ();
451                     }
452                   break;
453                 case '*':
454                   do_mul ();
455                   break;
456                 case 'm':
457                   do_mulm ();
458                   break;
459                 case '/':
460                   do_div ();
461                   break;
462                 case '%':
463                   do_rem ();
464                   break;
465                 case '^':
466                   do_powm ();
467                   break;
468                 case '>':
469                   do_rshift ();
470                   break;
471                 case 'I':
472                   do_inv ();
473                   break;
474                 case 'G':
475                   do_gcd ();
476                   break;
477                 case 'i':       /* dummy */
478                   if (!stackidx)
479                     fputs ("stack underflow\n", stderr);
480                   else
481                     {
482                       mpi_release (stack[stackidx - 1]);
483                       stackidx--;
484                     }
485                   break;
486                 case 'd':       /* duplicate the tos */
487                   if (!stackidx)
488                     fputs ("stack underflow\n", stderr);
489                   else if (stackidx < STACKSIZE)
490                     {
491                       mpi_release (stack[stackidx]);
492                       stack[stackidx] = mpi_copy (stack[stackidx - 1]);
493                       stackidx++;
494                     }
495                   else
496                     fputs ("stack overflow\n", stderr);
497                   break;
498                 case 'r':       /* swap top elements */
499                   if (stackidx < 2)
500                     fputs ("stack underflow\n", stderr);
501                   else if (stackidx < STACKSIZE)
502                     {
503                       gcry_mpi_t tmp = stack[stackidx-1];
504                       stack[stackidx-1] = stack[stackidx - 2];
505                       stack[stackidx-2] = tmp;
506                     }
507                   break;
508                 case 'b':
509                   do_nbits ();
510                   break;
511                 case 'c':
512                   for (i = 0; i < stackidx; i++)
513                     {
514                       mpi_release (stack[i]); stack[i] = NULL;
515                     }
516                   stackidx = 0;
517                   break;
518                 case 'p':       /* print the tos */
519                   if (!stackidx)
520                     puts ("stack is empty");
521                   else
522                     {
523                       print_mpi (stack[stackidx - 1]);
524                       putchar ('\n');
525                     }
526                   break;
527                 case 'f':       /* print the stack */
528                   for (i = stackidx - 1; i >= 0; i--)
529                     {
530                       printf ("[%2d]: ", i);
531                       print_mpi (stack[i]);
532                       putchar ('\n');
533                     }
534                   break;
535                 case '?':
536                   print_help ();
537                   break;
538                 default:
539                   fputs ("invalid operator\n", stderr);
540                 }
541             }
542         }
543       else if (state == 1) /* In a number. */
544         {
545           if (!isxdigit (c))
546             {
547               /* Store the number */
548               state = 0;
549               ungetc (c, stdin);
550               if (stridx < sizeof strbuf)
551                 strbuf[stridx] = 0;
552
553               if (stackidx < STACKSIZE)
554                 {
555                   if (!stack[stackidx])
556                     stack[stackidx] = mpi_new (0);
557                   if (scan_mpi (stack[stackidx], strbuf))
558                     fputs ("invalid number\n", stderr);
559                   else
560                     stackidx++;
561                 }
562               else
563                 fputs ("stack overflow\n", stderr);
564             }
565           else
566             { /* Store a digit.  */
567               if (stridx < sizeof strbuf - 1)
568                 strbuf[stridx++] = c;
569               else if (stridx == sizeof strbuf - 1)
570                 {
571                   strbuf[stridx] = 0;
572                   fputs ("input too large - truncated\n", stderr);
573                   stridx++;
574                 }
575             }
576         }
577       else if (state == 2) /* In a comment. */
578         {
579           if (c == '\n')
580             state = 0;
581         }
582
583     }
584
585   for (i = 0; i < stackidx; i++)
586     mpi_release (stack[i]);
587   return 0;
588 }