1
2/*
3 *  M_APM  -  mapm_set.c
4 *
5 *  Copyright (C) 1999 - 2007   Michael C. Ring
6 *
7 *  Permission to use, copy, and distribute this software and its
8 *  documentation for any purpose with or without fee is hereby granted,
9 *  provided that the above copyright notice appear in all copies and
10 *  that both that copyright notice and this permission notice appear
11 *  in supporting documentation.
12 *
13 *  Permission to modify the software is granted. Permission to distribute
14 *  the modified code is granted. Modifications are to be distributed by
15 *  using the file 'license.txt' as a template to modify the file header.
16 *  'license.txt' is available in the official MAPM distribution.
17 *
18 *  This software is provided "as is" without express or implied warranty.
19 */
20
21/*
22 *      $Id: mapm_set.c,v 1.18 2007/12/03 01:47:50 mike Exp $
23 *
24 *      This file contains the functions necessary to get C 'longs' and
25 *	'strings' into the MAPM number system. It also contains the function
26 *	to get a string from a MAPM number.
27 *
28 *      $Log: mapm_set.c,v $
29 *      Revision 1.18  2007/12/03 01:47:50  mike
30 *      Update license
31 *
32 *      Revision 1.17  2003/07/21 20:25:06  mike
33 *      Modify error messages to be in a consistent format.
34 *
35 *      Revision 1.16  2003/03/31 21:59:52  mike
36 *      call generic error handling function
37 *
38 *      Revision 1.15  2002/11/05 23:31:54  mike
39 *      use new set_to_zero call instead of copy
40 *
41 *      Revision 1.14  2002/11/03 22:24:19  mike
42 *      Updated function parameters to use the modern style
43 *
44 *      Revision 1.13  2001/07/16 19:34:16  mike
45 *      add function M_free_all_set
46 *
47 *      Revision 1.12  2001/02/11 22:33:27  mike
48 *      modify parameters to REALLOC
49 *
50 *      Revision 1.11  2001/01/23 21:16:03  mike
51 *      use dedicated call to long->ascii instead of sprintf
52 *
53 *      Revision 1.10  2000/10/25 22:57:25  mike
54 *      add cast which really wasn't needed
55 *
56 *      Revision 1.9  2000/10/25 19:57:01  mike
57 *      add free call to end of set string if the temp
58 *      string gets too big
59 *
60 *      Revision 1.8  2000/05/04 23:49:19  mike
61 *      put in more efficient set_long function
62 *
63 *      Revision 1.7  2000/02/03 22:47:15  mike
64 *      use MAPM_* generic memory function
65 *
66 *      Revision 1.6  1999/07/12 22:23:17  mike
67 *      tweak output string when input == 0
68 *
69 *      Revision 1.5  1999/07/12 02:07:56  mike
70 *      fix dec_places error (was == -1, should be < 0)
71 *
72 *      Revision 1.4  1999/06/19 21:36:57  mike
73 *      added some comments
74 *
75 *      Revision 1.3  1999/06/19 21:35:19  mike
76 *      changed local static variables to MAPM stack variables
77 *
78 *      Revision 1.2  1999/05/13 21:32:41  mike
79 *      added check for illegal chars in string parse
80 *
81 *      Revision 1.1  1999/05/10 20:56:31  mike
82 *      Initial revision
83 */
84
85#include "m_apm_lc.h"
86
87static	char *M_buf  = NULL;
88static  int   M_lbuf = 0;
89static  char *M_set_string_error_msg = "\'m_apm_set_string\', Out of memory";
90
91/****************************************************************************/
92void	M_free_all_set()
93{
94if (M_lbuf != 0)
95  {
96   MAPM_FREE(M_buf);
97   M_buf  = NULL;
98   M_lbuf = 0;
99  }
100}
101/****************************************************************************/
102void	m_apm_set_long(M_APM atmp, long mm)
103{
104int     len, ii, nbytes;
105char	*p, *buf, ch, buf2[64];
106
107/* if zero, return right away */
108
109if (mm == 0)
110  {
111   M_set_to_zero(atmp);
112   return;
113  }
114
115M_long_2_ascii(buf2, mm);     /* convert long -> ascii in base 10 */
116buf = buf2;
117
118if (mm < 0)
119  {
120   atmp->m_apm_sign = -1;
121   buf++;                     /* get past '-' sign */
122  }
123else
124  {
125   atmp->m_apm_sign = 1;
126  }
127
128len = strlen(buf);
129atmp->m_apm_exponent = len;
130
131/* least significant nibble of ODD data-length must be 0 */
132
133if ((len & 1) != 0)
134  {
135   buf[len] = '0';
136  }
137
138/* remove any trailing '0' ... */
139
140while (TRUE)
141  {
142   if (buf[--len] != '0')
143     break;
144  }
145
146atmp->m_apm_datalength = ++len;
147
148nbytes = (len + 1) >> 1;
149p = buf;
150
151for (ii=0; ii < nbytes; ii++)
152  {
153   ch = *p++ - '0';
154   atmp->m_apm_data[ii] = 10 * ch + *p++ - '0';
155  }
156}
157/****************************************************************************/
158void	m_apm_set_string(M_APM ctmp, char *s_in)
159{
160char	ch, *cp, *s, *p;
161void	*vp;
162int	i, j, zflag, exponent, sign;
163
164if (M_lbuf == 0)
165  {
166   M_lbuf = 256;
167   if ((M_buf = (char *)MAPM_MALLOC(256)) == NULL)
168     {
169      /* fatal, this does not return */
170
171      M_apm_log_error_msg(M_APM_FATAL, M_set_string_error_msg);
172     }
173  }
174
175if ((i = strlen(s_in)) > (M_lbuf - 4))
176  {
177   M_lbuf = i + 32;
178   if ((vp = MAPM_REALLOC(M_buf, M_lbuf)) == NULL)
179     {
180      /* fatal, this does not return */
181
182      M_apm_log_error_msg(M_APM_FATAL, M_set_string_error_msg);
183     }
184
185   M_buf = (char *)vp;
186  }
187
188s = M_buf;
189strcpy(s,s_in);
190
191/* default == zero ... */
192
193M_set_to_zero(ctmp);
194
195p = s;
196
197while (TRUE)
198  {
199   if (*p == ' ' || *p == '\t')
200     p++;
201   else
202     break;
203  }
204
205if (*p == '\0')
206  return;
207
208sign = 1;             /* assume number is positive */
209
210if (*p == '+')        /* scan by optional '+' sign */
211  p++;
212else
213  {
214   if (*p == '-')     /* check if number negative */
215     {
216      sign = -1;
217      p++;
218     }
219  }
220
221M_lowercase(p);       /* convert string to lowercase */
222exponent = 0;         /* default */
223
224if ((cp = strstr(p,"e")) != NULL)
225  {
226   exponent = atoi(cp + sizeof(char));
227   *cp = '\0';          /* erase the exponent now */
228  }
229
230j = M_strposition(p,".");        /* is there a decimal point ?? */
231if (j == -1)
232  {
233   strcat(p,".");                /* if not, append one */
234   j = M_strposition(p,".");     /* now find it ... */
235  }
236
237if (j > 0)                       /* normalize number and adjust exponent */
238  {
239   exponent += j;
240   memmove((p+1),p,(j * sizeof(char)));
241  }
242
243p++;        /* scan past implied decimal point now in column 1 (index 0) */
244
245i = strlen(p);
246ctmp->m_apm_datalength = i;
247
248if ((i & 1) != 0)   /* if odd number of digits, append a '0' to make it even */
249  strcat(p,"0");
250
251j = strlen(p) >> 1;  /* number of bytes in encoded M_APM number */
252
253/* do we need more memory to hold this number */
254
255if (j > ctmp->m_apm_malloclength)
256  {
257   if ((vp = MAPM_REALLOC(ctmp->m_apm_data, (j + 32))) == NULL)
258     {
259      /* fatal, this does not return */
260
261      M_apm_log_error_msg(M_APM_FATAL, M_set_string_error_msg);
262     }
263
264   ctmp->m_apm_malloclength = j + 28;
265   ctmp->m_apm_data = (UCHAR *)vp;
266  }
267
268zflag = TRUE;
269
270for (i=0; i < j; i++)
271  {
272   ch = *p++ - '0';
273   if ((ch = (10 * ch + *p++ - '0')) != 0)
274     zflag = FALSE;
275
276   if (((int)ch & 0xFF) >= 100)
277     {
278      M_apm_log_error_msg(M_APM_RETURN,
279      "\'m_apm_set_string\', Non-digit char found in parse");
280
281      M_apm_log_error_msg(M_APM_RETURN, "Text =");
282      M_apm_log_error_msg(M_APM_RETURN, s_in);
283
284      M_set_to_zero(ctmp);
285      return;
286     }
287
288   ctmp->m_apm_data[i]   = ch;
289   ctmp->m_apm_data[i+1] = 0;
290  }
291
292ctmp->m_apm_exponent = exponent;
293ctmp->m_apm_sign     = sign;
294
295if (zflag)
296  {
297   ctmp->m_apm_exponent   = 0;
298   ctmp->m_apm_sign       = 0;
299   ctmp->m_apm_datalength = 1;
300  }
301else
302  {
303   M_apm_normalize(ctmp);
304  }
305
306/*
307 *  if our local temp string is getting too big,
308 *  release it's memory and start over next time.
309 *  (this 1000 byte threshold is quite arbitrary,
310 *  it may be more efficient in your app to make
311 *  this number bigger).
312 */
313
314if (M_lbuf > 1000)
315  {
316   MAPM_FREE(M_buf);
317   M_buf  = NULL;
318   M_lbuf = 0;
319  }
320}
321/****************************************************************************/
322void	m_apm_to_string(char *s, int places, M_APM mtmp)
323{
324M_APM   ctmp;
325char	*cp;
326int	i, index, first, max_i, num_digits, dec_places;
327UCHAR	numdiv, numrem;
328
329ctmp = M_get_stack_var();
330dec_places = places;
331
332if (dec_places < 0)
333  m_apm_copy(ctmp, mtmp);
334else
335  m_apm_round(ctmp, dec_places, mtmp);
336
337if (ctmp->m_apm_sign == 0)
338  {
339   if (dec_places < 0)
340      strcpy(s,"0.0E+0");
341   else
342     {
343      strcpy(s,"0");
344
345      if (dec_places > 0)
346        strcat(s,".");
347
348      for (i=0; i < dec_places; i++)
349        strcat(s,"0");
350
351      strcat(s,"E+0");
352     }
353
354   M_restore_stack(1);
355   return;
356  }
357
358max_i = (ctmp->m_apm_datalength + 1) >> 1;
359
360if (dec_places < 0)
361  num_digits = ctmp->m_apm_datalength;
362else
363  num_digits = dec_places + 1;
364
365cp = s;
366
367if (ctmp->m_apm_sign == -1)
368  *cp++ = '-';
369
370first = TRUE;
371
372i = 0;
373index = 0;
374
375while (TRUE)
376  {
377   if (index >= max_i)
378     {
379      numdiv = 0;
380      numrem = 0;
381     }
382   else
383      M_get_div_rem_10((int)ctmp->m_apm_data[index],&numdiv,&numrem);
384
385   index++;
386
387   *cp++ = numdiv + '0';
388
389   if (++i == num_digits)
390     break;
391
392   if (first)
393     {
394      first = FALSE;
395      *cp++ = '.';
396     }
397
398   *cp++ = numrem + '0';
399
400   if (++i == num_digits)
401     break;
402  }
403
404i = ctmp->m_apm_exponent - 1;
405if (i >= 0)
406  sprintf(cp,"E+%d",i);
407else
408  sprintf(cp,"E%d",i);
409
410M_restore_stack(1);
411}
412/****************************************************************************/
413