xref: /PHP-8.1/ext/bcmath/libbcmath/src/output.c (revision 57a8bf62)
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 
bc_out_long(long val,size_t size,bool space,void (* out_char)(char))59 void bc_out_long (long val, size_t size, bool space, void (*out_char)(char) )
60 {
61   char digits[40];
62   size_t len, ix;
63 
64   if (space) (*out_char) (' ');
65   snprintf(digits, sizeof(digits), "%ld", val);
66   len = strlen (digits);
67   while (size > len)
68     {
69       (*out_char) ('0');
70       size--;
71     }
72   for (ix=0; ix < len; ix++)
73     (*out_char) (digits[ix]);
74 }
75 
76 /* Output of a bcd number.  NUM is written in base O_BASE using OUT_CHAR
77    as the routine to do the actual output of the characters. */
78 
bc_out_num(bc_num num,int o_base,void (* out_char)(char),int leading_zero)79 void bc_out_num (bc_num num, int o_base, void (*out_char)(char), int leading_zero)
80 {
81   char *nptr;
82   int  index, fdigit;
83   bool pre_space;
84   stk_rec *digits, *temp;
85   bc_num int_part, frac_part, base, cur_dig, t_num, max_o_digit;
86 
87   /* The negative sign if needed. */
88   if (num->n_sign == MINUS) (*out_char) ('-');
89 
90   /* Output the number. */
91   if (bc_is_zero (num))
92     (*out_char) ('0');
93   else
94     if (o_base == 10)
95       {
96 	/* The number is in base 10, do it the fast way. */
97 	nptr = num->n_value;
98 	if (num->n_len > 1 || *nptr != 0)
99 	  for (index=num->n_len; index>0; index--)
100 	    (*out_char) (BCD_CHAR(*nptr++));
101 	else
102 	  nptr++;
103 
104 	if (leading_zero && bc_is_zero (num))
105 	  (*out_char) ('0');
106 
107 	/* Now the fraction. */
108 	if (num->n_scale > 0)
109 	  {
110 	    (*out_char) ('.');
111 	    for (index=0; index<num->n_scale; index++)
112 	      (*out_char) (BCD_CHAR(*nptr++));
113 	  }
114       }
115     else
116       {
117 	/* special case ... */
118 	if (leading_zero && bc_is_zero (num))
119 	  (*out_char) ('0');
120 
121 	/* The number is some other base. */
122 	digits = NULL;
123 	bc_init_num (&int_part);
124 	bc_divide (num, BCG(_one_), &int_part, 0);
125 	bc_init_num (&frac_part);
126 	bc_init_num (&cur_dig);
127 	bc_init_num (&base);
128 	bc_sub (num, int_part, &frac_part, 0);
129 	/* Make the INT_PART and FRAC_PART positive. */
130 	int_part->n_sign = PLUS;
131 	frac_part->n_sign = PLUS;
132 	bc_int2num (&base, o_base);
133 	bc_init_num (&max_o_digit);
134 	bc_int2num (&max_o_digit, o_base-1);
135 
136 
137 	/* Get the digits of the integer part and push them on a stack. */
138 	while (!bc_is_zero (int_part))
139 	  {
140 	    bc_modulo (int_part, base, &cur_dig, 0);
141 		/* PHP Change:  malloc() -> emalloc() */
142 	    temp = (stk_rec *) emalloc (sizeof(stk_rec));
143 	    temp->digit = bc_num2long (cur_dig);
144 	    temp->next = digits;
145 	    digits = temp;
146 	    bc_divide (int_part, base, &int_part, 0);
147 	  }
148 
149 	/* Print the digits on the stack. */
150 	if (digits != NULL)
151 	  {
152 	    /* Output the digits. */
153 	    while (digits != NULL)
154 	      {
155 		temp = digits;
156 		digits = digits->next;
157 		if (o_base <= 16)
158 		  (*out_char) (ref_str[ (int) temp->digit]);
159 		else
160 		  bc_out_long (temp->digit, max_o_digit->n_len, 1, out_char);
161 		efree (temp);
162 	      }
163 	  }
164 
165 	/* Get and print the digits of the fraction part. */
166 	if (num->n_scale > 0)
167 	  {
168 	    (*out_char) ('.');
169 	    pre_space = false;
170 	    t_num = bc_copy_num (BCG(_one_));
171 	    while (t_num->n_len <= num->n_scale) {
172 	      bc_multiply (frac_part, base, &frac_part, num->n_scale);
173 	      fdigit = bc_num2long (frac_part);
174 	      bc_int2num (&int_part, fdigit);
175 	      bc_sub (frac_part, int_part, &frac_part, 0);
176 	      if (o_base <= 16)
177 		(*out_char) (ref_str[fdigit]);
178 	      else {
179 		bc_out_long (fdigit, max_o_digit->n_len, pre_space, out_char);
180 		pre_space = true;
181 	      }
182 	      bc_multiply (t_num, base, &t_num, 0);
183 	    }
184 	    bc_free_num (&t_num);
185 	  }
186 
187 	/* Clean up. */
188 	bc_free_num (&int_part);
189 	bc_free_num (&frac_part);
190 	bc_free_num (&base);
191 	bc_free_num (&cur_dig);
192 	bc_free_num (&max_o_digit);
193       }
194 }
195