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