xref: /PHP-7.4/ext/bcmath/libbcmath/src/output.c (revision eb9ad31b)
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.  (LICENSE)
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 <stdlib.h>
35 #include <ctype.h>
36 #include <stdarg.h>
37 #include "bcmath.h"
38 #include "private.h"
39 
40 
41 /* The following routines provide output for bcd numbers package
42    using the rules of POSIX bc for output. */
43 
44 /* This structure is used for saving digits in the conversion process. */
45 typedef struct stk_rec {
46 	long  digit;
47 	struct stk_rec *next;
48 } stk_rec;
49 
50 /* The reference string for digits. */
51 static char ref_str[] = "0123456789ABCDEF";
52 
53 
54 /* A special output routine for "multi-character digits."  Exactly
55    SIZE characters must be output for the value VAL.  If SPACE is
56    non-zero, we must output one space before the number.  OUT_CHAR
57    is the actual routine for writing the characters. */
58 
59 void
bc_out_long(val,size,space,out_char)60 bc_out_long (val, size, space, out_char)
61      long val;
62      int size, space;
63 #ifdef __STDC__
64      void (*out_char)(int);
65 #else
66      void (*out_char)();
67 #endif
68 {
69   char digits[40];
70   int len, ix;
71 
72   if (space) (*out_char) (' ');
73   snprintf(digits, sizeof(digits), "%ld", val);
74   len = strlen (digits);
75   while (size > len)
76     {
77       (*out_char) ('0');
78       size--;
79     }
80   for (ix=0; ix < len; ix++)
81     (*out_char) (digits[ix]);
82 }
83 
84 /* Output of a bcd number.  NUM is written in base O_BASE using OUT_CHAR
85    as the routine to do the actual output of the characters. */
86 
87 void
88 #ifdef __STDC__
bc_out_num(bc_num num,int o_base,void (* out_char)(int),int leading_zero)89 bc_out_num (bc_num num, int o_base, void (*out_char)(int), int leading_zero)
90 #else
91 bc_out_num (bc_num num, int o_base, void (*out_char)(), int leading_zero)
92 #endif
93 {
94   char *nptr;
95   int  index, fdigit, pre_space;
96   stk_rec *digits, *temp;
97   bc_num int_part, frac_part, base, cur_dig, t_num, max_o_digit;
98 
99   /* The negative sign if needed. */
100   if (num->n_sign == MINUS) (*out_char) ('-');
101 
102   /* Output the number. */
103   if (bc_is_zero (num))
104     (*out_char) ('0');
105   else
106     if (o_base == 10)
107       {
108 	/* The number is in base 10, do it the fast way. */
109 	nptr = num->n_value;
110 	if (num->n_len > 1 || *nptr != 0)
111 	  for (index=num->n_len; index>0; index--)
112 	    (*out_char) (BCD_CHAR(*nptr++));
113 	else
114 	  nptr++;
115 
116 	if (leading_zero && bc_is_zero (num))
117 	  (*out_char) ('0');
118 
119 	/* Now the fraction. */
120 	if (num->n_scale > 0)
121 	  {
122 	    (*out_char) ('.');
123 	    for (index=0; index<num->n_scale; index++)
124 	      (*out_char) (BCD_CHAR(*nptr++));
125 	  }
126       }
127     else
128       {
129 	/* special case ... */
130 	if (leading_zero && bc_is_zero (num))
131 	  (*out_char) ('0');
132 
133 	/* The number is some other base. */
134 	digits = NULL;
135 	bc_init_num (&int_part);
136 	bc_divide (num, BCG(_one_), &int_part, 0);
137 	bc_init_num (&frac_part);
138 	bc_init_num (&cur_dig);
139 	bc_init_num (&base);
140 	bc_sub (num, int_part, &frac_part, 0);
141 	/* Make the INT_PART and FRAC_PART positive. */
142 	int_part->n_sign = PLUS;
143 	frac_part->n_sign = PLUS;
144 	bc_int2num (&base, o_base);
145 	bc_init_num (&max_o_digit);
146 	bc_int2num (&max_o_digit, o_base-1);
147 
148 
149 	/* Get the digits of the integer part and push them on a stack. */
150 	while (!bc_is_zero (int_part))
151 	  {
152 	    bc_modulo (int_part, base, &cur_dig, 0);
153 		/* PHP Change:  malloc() -> emalloc() */
154 	    temp = (stk_rec *) emalloc (sizeof(stk_rec));
155 	    temp->digit = bc_num2long (cur_dig);
156 	    temp->next = digits;
157 	    digits = temp;
158 	    bc_divide (int_part, base, &int_part, 0);
159 	  }
160 
161 	/* Print the digits on the stack. */
162 	if (digits != NULL)
163 	  {
164 	    /* Output the digits. */
165 	    while (digits != NULL)
166 	      {
167 		temp = digits;
168 		digits = digits->next;
169 		if (o_base <= 16)
170 		  (*out_char) (ref_str[ (int) temp->digit]);
171 		else
172 		  bc_out_long (temp->digit, max_o_digit->n_len, 1, out_char);
173 		efree (temp);
174 	      }
175 	  }
176 
177 	/* Get and print the digits of the fraction part. */
178 	if (num->n_scale > 0)
179 	  {
180 	    (*out_char) ('.');
181 	    pre_space = 0;
182 	    t_num = bc_copy_num (BCG(_one_));
183 	    while (t_num->n_len <= num->n_scale) {
184 	      bc_multiply (frac_part, base, &frac_part, num->n_scale);
185 	      fdigit = bc_num2long (frac_part);
186 	      bc_int2num (&int_part, fdigit);
187 	      bc_sub (frac_part, int_part, &frac_part, 0);
188 	      if (o_base <= 16)
189 		(*out_char) (ref_str[fdigit]);
190 	      else {
191 		bc_out_long (fdigit, max_o_digit->n_len, pre_space, out_char);
192 		pre_space = 1;
193 	      }
194 	      bc_multiply (t_num, base, &t_num, 0);
195 	    }
196 	    bc_free_num (&t_num);
197 	  }
198 
199 	/* Clean up. */
200 	bc_free_num (&int_part);
201 	bc_free_num (&frac_part);
202 	bc_free_num (&base);
203 	bc_free_num (&cur_dig);
204 	bc_free_num (&max_o_digit);
205       }
206 }
207