xref: /PHP-7.3/ext/bcmath/libbcmath/src/output.c (revision edcabf6d)
1 /* output.c: bcmath library file. */
2 /*
3     Copyright (C) 1991, 1992, 1993, 1994, 1997 Free Software Foundation, Inc.
4     Copyright (C) 2000 Philip A. Nelson
5 
6     This library is free software; you can redistribute it and/or
7     modify it under the terms of the GNU Lesser General Public
8     License as published by the Free Software Foundation; either
9     version 2 of the License, or (at your option) any later version.
10 
11     This library is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14     Lesser General Public License for more details.  (COPYING.LIB)
15 
16     You should have received a copy of the GNU Lesser General Public
17     License along with this library; if not, write to:
18 
19       The Free Software Foundation, Inc.
20       59 Temple Place, Suite 330
21       Boston, MA 02111-1307 USA.
22 
23     You may contact the author by:
24        e-mail:  philnelson@acm.org
25       us-mail:  Philip A. Nelson
26                 Computer Science Department, 9062
27                 Western Washington University
28                 Bellingham, WA 98226-9062
29 
30 *************************************************************************/
31 
32 #include <config.h>
33 #include <stdio.h>
34 #include <assert.h>
35 #include <stdlib.h>
36 #include <ctype.h>
37 #include <stdarg.h>
38 #include "bcmath.h"
39 #include "private.h"
40 
41 
42 /* The following routines provide output for bcd numbers package
43    using the rules of POSIX bc for output. */
44 
45 /* This structure is used for saving digits in the conversion process. */
46 typedef struct stk_rec {
47 	long  digit;
48 	struct stk_rec *next;
49 } stk_rec;
50 
51 /* The reference string for digits. */
52 static char ref_str[] = "0123456789ABCDEF";
53 
54 
55 /* A special output routine for "multi-character digits."  Exactly
56    SIZE characters must be output for the value VAL.  If SPACE is
57    non-zero, we must output one space before the number.  OUT_CHAR
58    is the actual routine for writing the characters. */
59 
60 void
bc_out_long(val,size,space,out_char)61 bc_out_long (val, size, space, out_char)
62      long val;
63      int size, space;
64 #ifdef __STDC__
65      void (*out_char)(int);
66 #else
67      void (*out_char)();
68 #endif
69 {
70   char digits[40];
71   int len, ix;
72 
73   if (space) (*out_char) (' ');
74   snprintf(digits, sizeof(digits), "%ld", val);
75   len = strlen (digits);
76   while (size > len)
77     {
78       (*out_char) ('0');
79       size--;
80     }
81   for (ix=0; ix < len; ix++)
82     (*out_char) (digits[ix]);
83 }
84 
85 /* Output of a bcd number.  NUM is written in base O_BASE using OUT_CHAR
86    as the routine to do the actual output of the characters. */
87 
88 void
89 #ifdef __STDC__
bc_out_num(bc_num num,int o_base,void (* out_char)(int),int leading_zero)90 bc_out_num (bc_num num, int o_base, void (*out_char)(int), int leading_zero)
91 #else
92 bc_out_num (bc_num num, int o_base, void (*out_char)(), int leading_zero)
93 #endif
94 {
95   char *nptr;
96   int  index, fdigit, pre_space;
97   stk_rec *digits, *temp;
98   bc_num int_part, frac_part, base, cur_dig, t_num, max_o_digit;
99 
100   /* The negative sign if needed. */
101   if (num->n_sign == MINUS) (*out_char) ('-');
102 
103   /* Output the number. */
104   if (bc_is_zero (num))
105     (*out_char) ('0');
106   else
107     if (o_base == 10)
108       {
109 	/* The number is in base 10, do it the fast way. */
110 	nptr = num->n_value;
111 	if (num->n_len > 1 || *nptr != 0)
112 	  for (index=num->n_len; index>0; index--)
113 	    (*out_char) (BCD_CHAR(*nptr++));
114 	else
115 	  nptr++;
116 
117 	if (leading_zero && bc_is_zero (num))
118 	  (*out_char) ('0');
119 
120 	/* Now the fraction. */
121 	if (num->n_scale > 0)
122 	  {
123 	    (*out_char) ('.');
124 	    for (index=0; index<num->n_scale; index++)
125 	      (*out_char) (BCD_CHAR(*nptr++));
126 	  }
127       }
128     else
129       {
130 	/* special case ... */
131 	if (leading_zero && bc_is_zero (num))
132 	  (*out_char) ('0');
133 
134 	/* The number is some other base. */
135 	digits = NULL;
136 	bc_init_num (&int_part);
137 	bc_divide (num, BCG(_one_), &int_part, 0);
138 	bc_init_num (&frac_part);
139 	bc_init_num (&cur_dig);
140 	bc_init_num (&base);
141 	bc_sub (num, int_part, &frac_part, 0);
142 	/* Make the INT_PART and FRAC_PART positive. */
143 	int_part->n_sign = PLUS;
144 	frac_part->n_sign = PLUS;
145 	bc_int2num (&base, o_base);
146 	bc_init_num (&max_o_digit);
147 	bc_int2num (&max_o_digit, o_base-1);
148 
149 
150 	/* Get the digits of the integer part and push them on a stack. */
151 	while (!bc_is_zero (int_part))
152 	  {
153 	    bc_modulo (int_part, base, &cur_dig, 0);
154 		/* PHP Change:  malloc() -> emalloc() */
155 	    temp = (stk_rec *) emalloc (sizeof(stk_rec));
156 	    temp->digit = bc_num2long (cur_dig);
157 	    temp->next = digits;
158 	    digits = temp;
159 	    bc_divide (int_part, base, &int_part, 0);
160 	  }
161 
162 	/* Print the digits on the stack. */
163 	if (digits != NULL)
164 	  {
165 	    /* Output the digits. */
166 	    while (digits != NULL)
167 	      {
168 		temp = digits;
169 		digits = digits->next;
170 		if (o_base <= 16)
171 		  (*out_char) (ref_str[ (int) temp->digit]);
172 		else
173 		  bc_out_long (temp->digit, max_o_digit->n_len, 1, out_char);
174 		efree (temp);
175 	      }
176 	  }
177 
178 	/* Get and print the digits of the fraction part. */
179 	if (num->n_scale > 0)
180 	  {
181 	    (*out_char) ('.');
182 	    pre_space = 0;
183 	    t_num = bc_copy_num (BCG(_one_));
184 	    while (t_num->n_len <= num->n_scale) {
185 	      bc_multiply (frac_part, base, &frac_part, num->n_scale);
186 	      fdigit = bc_num2long (frac_part);
187 	      bc_int2num (&int_part, fdigit);
188 	      bc_sub (frac_part, int_part, &frac_part, 0);
189 	      if (o_base <= 16)
190 		(*out_char) (ref_str[fdigit]);
191 	      else {
192 		bc_out_long (fdigit, max_o_digit->n_len, pre_space, out_char);
193 		pre_space = 1;
194 	      }
195 	      bc_multiply (t_num, base, &t_num, 0);
196 	    }
197 	    bc_free_num (&t_num);
198 	  }
199 
200 	/* Clean up. */
201 	bc_free_num (&int_part);
202 	bc_free_num (&frac_part);
203 	bc_free_num (&base);
204 	bc_free_num (&cur_dig);
205 	bc_free_num (&max_o_digit);
206       }
207 }
208