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