See ChangeLog: Mon Jan 31 17:48:10 CET 2000 Werner Koch
[gnupg.git] / tools / mpicalc.c
1 /* mpitest.c - test the mpi functions
2  *      Copyright (C) 1998 Free Software Foundation, Inc.
3  *
4  * This is an RPN calculator; values must be given in hex.
5  * Operation is like dc(1) except that the input/output radix is
6  * always 16 and you can use a '-' to prefix a negative number.
7  * Addition operators: ++ and --. All operators must be delimited by a blank
8  *
9  *
10  * This file is part of GnuPG.
11  *
12  * GnuPG is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * GnuPG is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
25  */
26
27 #include <config.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <ctype.h>
31 #include <gcrypt.h>
32
33 #include "util.h"
34 #include "i18n.h"
35
36 #define STACKSIZE  100
37 static MPI stack[STACKSIZE];
38 static int stackidx;
39
40
41 static const char *
42 my_strusage( int level )
43 {
44     const char *p;
45     switch( level ) {
46       case 10:
47       case 0:   p = "mpicalc - v" VERSION "; "
48                     "Copyright 1997 Werner Koch (dd9jn)" ; break;
49       case 13:  p = "mpicalc"; break;
50       case 14:  p = VERSION; break;
51       case 1:
52       case 11:  p = "Usage: mpicalc (-h for help)";
53                 break;
54       case 2:
55       case 12:  p =
56     "\nSyntax: mpicalc [options] [files]\n"
57     "MPI RPN calculator\n";
58         break;
59       default:  p = NULL;
60     }
61     return p;
62 }
63
64
65 static void
66 i18n_init(void)
67 {
68   #ifdef ENABLE_NLS
69     #ifdef HAVE_LC_MESSAGES
70        setlocale( LC_MESSAGES, "" );
71     #else
72        setlocale( LC_ALL, "" );
73     #endif
74     bindtextdomain( PACKAGE, GNUPG_LOCALEDIR );
75     textdomain( PACKAGE );
76   #endif
77 }
78
79 int
80 mpi_print( FILE *fp, MPI a, int mode )
81 {
82     int n=0;
83
84     if( !a )
85         return fprintf(fp, "[MPI_NULL]");
86     if( !mode ) {
87         unsigned int n1;
88         n1 = gcry_mpi_get_nbits(a);
89         n += fprintf(fp, "[%u bits]", n1);
90     }
91     else {
92         int rc;
93         char *buffer;
94
95         rc = gcry_mpi_aprint( GCRYMPI_FMT_HEX, (void **)&buffer, NULL, a );
96         fputs( buffer, fp );
97         n += strlen(buffer);
98         gcry_free( buffer );
99     }
100     return n;
101 }
102
103
104 static void
105 do_add(void)
106 {
107     if( stackidx < 2 ) {
108         fputs("stack underflow\n",stderr);
109         return;
110     }
111     mpi_add( stack[stackidx-2], stack[stackidx-2], stack[stackidx-1] );
112     stackidx--;
113 }
114
115 static void
116 do_sub(void)
117 {
118     if( stackidx < 2 ) {
119         fputs("stack underflow\n", stderr);
120         return;
121     }
122     mpi_sub( stack[stackidx-2], stack[stackidx-2], stack[stackidx-1] );
123     stackidx--;
124 }
125
126 static void
127 do_inc(void)
128 {
129     if( stackidx < 1 ) {
130         fputs("stack underflow\n", stderr);
131         return;
132     }
133     mpi_add_ui( stack[stackidx-1], stack[stackidx-1], 1 );
134 }
135
136 static void
137 do_dec(void)
138 {
139     if( stackidx < 1 ) {
140         fputs("stack underflow\n", stderr);
141         return;
142     }
143  /* mpi_sub_ui( stack[stackidx-1], stack[stackidx-1], 1 ); */
144 }
145
146 static void
147 do_mul(void)
148 {
149     if( stackidx < 2 ) {
150         fputs("stack underflow\n", stderr);
151         return;
152     }
153     mpi_mul( stack[stackidx-2], stack[stackidx-2], stack[stackidx-1] );
154     stackidx--;
155 }
156
157 static void
158 do_mulm(void)
159 {
160     if( stackidx < 3 ) {
161         fputs("stack underflow\n", stderr);
162         return;
163     }
164     mpi_mulm( stack[stackidx-3], stack[stackidx-3],
165                                  stack[stackidx-2], stack[stackidx-1] );
166     stackidx -= 2;
167 }
168
169 static void
170 do_div(void)
171 {
172     if( stackidx < 2 ) {
173         fputs("stack underflow\n", stderr);
174         return;
175     }
176     mpi_fdiv_q( stack[stackidx-2], stack[stackidx-2], stack[stackidx-1] );
177     stackidx--;
178 }
179
180 static void
181 do_rem(void)
182 {
183     if( stackidx < 2 ) {
184         fputs("stack underflow\n", stderr);
185         return;
186     }
187     mpi_fdiv_r( stack[stackidx-2], stack[stackidx-2], stack[stackidx-1] );
188     stackidx--;
189 }
190
191 static void
192 do_powm(void)
193 {
194     MPI a;
195     if( stackidx < 3 ) {
196         fputs("stack underflow\n", stderr);
197         return;
198     }
199     a= mpi_alloc(10);
200     mpi_powm( a, stack[stackidx-3], stack[stackidx-2], stack[stackidx-1] );
201     mpi_free(stack[stackidx-3]);
202     stack[stackidx-3] = a;
203     stackidx -= 2;
204 }
205
206 static void
207 do_inv(void)
208 {
209     MPI a = mpi_alloc(40);
210     if( stackidx < 2 ) {
211         fputs("stack underflow\n", stderr);
212         return;
213     }
214     mpi_invm( a, stack[stackidx-2], stack[stackidx-1] );
215     mpi_set(stack[stackidx-2],a);
216     mpi_free(a);
217     stackidx--;
218 }
219
220 static void
221 do_gcd(void)
222 {
223     MPI a = mpi_alloc(40);
224     if( stackidx < 2 ) {
225         fputs("stack underflow\n", stderr);
226         return;
227     }
228     mpi_gcd( a, stack[stackidx-2], stack[stackidx-1] );
229     mpi_set(stack[stackidx-2],a);
230     mpi_free(a);
231     stackidx--;
232 }
233
234 static void
235 do_rshift(void)
236 {
237     if( stackidx < 1 ) {
238         fputs("stack underflow\n", stderr);
239         return;
240     }
241     mpi_rshift( stack[stackidx-1],stack[stackidx-1], 1 );
242 }
243
244
245 int
246 main(int argc, char **argv)
247 {
248     static ARGPARSE_OPTS opts[] = {
249     {0} };
250     ARGPARSE_ARGS pargs;
251     int i, c;
252     int state = 0;
253     char strbuf[1000];
254     int stridx=0;
255
256     pargs.argc = &argc;
257     pargs.argv = &argv;
258     pargs.flags = 0;
259
260     set_strusage( my_strusage );
261     i18n_init();
262     while( arg_parse( &pargs, opts) ) {
263         switch( pargs.r_opt ) {
264           default : pargs.err = 2; break;
265         }
266     }
267     if( argc )
268         usage(1);
269
270
271     for(i=0; i < STACKSIZE; i++ )
272         stack[i] = NULL;
273     stackidx =0;
274
275     while( (c=getc(stdin)) != EOF ) {
276         if( !state ) {  /* waiting */
277             if( isdigit(c) ) {
278                 state = 1;
279                 ungetc(c, stdin);
280                 strbuf[0] = '0';
281                 strbuf[1] = 'x';
282                 stridx=2;
283             }
284             else if( isspace(c) )
285                 ;
286             else {
287                 switch(c) {
288                   case '+':
289                     if( (c=getc(stdin)) == '+' )
290                         do_inc();
291                     else {
292                         ungetc(c, stdin);
293                         do_add();
294                     }
295                     break;
296                   case '-':
297                     if( (c=getc(stdin)) == '-' )
298                         do_dec();
299                     else if( isdigit(c) || (c >='A' && c <= 'F') ) {
300                         state = 1;
301                         ungetc(c, stdin);
302                         strbuf[0] = '-';
303                         strbuf[1] = '0';
304                         strbuf[2] = 'x';
305                         stridx=3;
306                     }
307                     else {
308                         ungetc(c, stdin);
309                         do_sub();
310                     }
311                     break;
312                   case '*':
313                     do_mul();
314                     break;
315                   case 'm':
316                     do_mulm();
317                     break;
318                   case '/':
319                     do_div();
320                     break;
321                   case '%':
322                     do_rem();
323                     break;
324                   case '^':
325                     do_powm();
326                     break;
327                   case 'I':
328                     do_inv();
329                     break;
330                   case 'G':
331                     do_gcd();
332                     break;
333                   case '>':
334                     do_rshift();
335                     break;
336                   case 'i': /* dummy */
337                     if( !stackidx )
338                         fputs("stack underflow\n", stderr);
339                     else {
340                         mpi_free(stack[stackidx-1]);
341                         stackidx--;
342                     }
343                     break;
344                   case 'd': /* duplicate the tos */
345                     if( !stackidx )
346                         fputs("stack underflow\n", stderr);
347                     else if( stackidx < STACKSIZE ) {
348                         mpi_free(stack[stackidx]);
349                         stack[stackidx] = mpi_copy( stack[stackidx-1] );
350                         stackidx++;
351                     }
352                     else
353                         fputs("stack overflow\n", stderr);
354                     break;
355                   case 'c':
356                     for(i=0; i < stackidx; i++ )
357                         mpi_free(stack[i]), stack[i] = NULL;
358                     stackidx = 0;
359                     break;
360                   case 'p': /* print the tos */
361                     if( !stackidx )
362                         puts("stack is empty");
363                     else {
364                         mpi_print(stdout, stack[stackidx-1], 1 );
365                         putchar('\n');
366                     }
367                     break;
368                   case 'f': /* print the stack */
369                     for( i = stackidx-1 ; i >= 0; i-- ) {
370                         printf("[%2d]: ", i );
371                         mpi_print(stdout, stack[i], 1 );
372                         putchar('\n');
373                     }
374                     break;
375                   default:
376                     fputs("invalid operator\n", stderr);
377                 }
378             }
379         }
380         else if( state == 1 ) { /* in a number */
381             if( !isxdigit(c) ) { /* store the number */
382                 state = 0;
383                 ungetc(c, stdin);
384                 if( stridx < 1000 )
385                     strbuf[stridx] = 0;
386
387                 if( stackidx < STACKSIZE ) {
388                     if( !stack[stackidx] )
389                         stack[stackidx] = mpi_alloc(10);
390                     if( mpi_fromstr(stack[stackidx], strbuf) )
391                         fputs("invalid number\n", stderr);
392                     else
393                         stackidx++;
394                 }
395                 else
396                     fputs("stack overflow\n", stderr);
397             }
398             else { /* store digit */
399                 if( stridx < 999 )
400                     strbuf[stridx++] = c;
401                 else if( stridx == 999 ) {
402                     strbuf[stridx] = 0;
403                     fputs("string too large - truncated\n", stderr);
404                     stridx++;
405                 }
406             }
407         }
408
409     }
410     for(i=0; i < stackidx; i++ )
411         mpi_free(stack[i]);
412     return 0;
413 }
414
415