1 /* libgmp formatters */
2
3 /* we only need output here, so turn off other IO functions */
4 #define EX_UTILS_NO_USE_INPUT 1
5 #define EX_UTILS_NO_USE_OPEN 1
6 #include "ex_utils.h" /* helper functions */
7
8 #include <limits.h>
9 #include <gmp.h>
10 #include <locale.h>
11
12 /* if this is enabled we add the malloc()d string as a reference,
13 * which saves doing an extra copy. */
14 #define EX_GMP_NUMS_USE_REFS 0
15
16 /* do we want to test that it works? */
17 #define EX_GMP_NUMS_USE_TST 0
18
19 /* This is the custom formatter.
20 * Note that this deals with grouping unlike the gmp_*printf() calls */
ex__usr_mpz_cb(Vstr_base * base,size_t pos,Vstr_fmt_spec * spec,char fmt,const mpz_t val)21 static int ex__usr_mpz_cb(Vstr_base *base, size_t pos, Vstr_fmt_spec *spec,
22 char fmt, const mpz_t val)
23 {
24 unsigned int nb = 10;
25 int flags = VSTR_FLAG_SC_FMT_CB_BEG_OBJ_NUM; /* it's a number */
26 size_t len = 0;
27 int ret = FALSE;
28 char ui_buf[(sizeof(unsigned long) * CHAR_BIT) + 1];
29 char *buf = NULL;
30 char *out_buf = ui_buf;
31
32 switch (fmt)
33 {
34 case 'd': break;
35 case 'x': nb = 16; flags |= VSTR_FLAG_SC_FMT_CB_BEG_OBJ_HEXNUM_L; break;
36 case 'o': nb = 8; flags |= VSTR_FLAG_SC_FMT_CB_BEG_OBJ_OCTNUM; break;
37 case 'b': nb = 2; flags |= VSTR_FLAG_SC_FMT_CB_BEG_OBJ_BINNUM_L; break;
38 default: ASSERT(FALSE); break;
39 }
40
41 if (mpz_sgn(val) == -1)
42 flags |= VSTR_FLAG_SC_FMT_CB_BEG_OBJ_NEG;
43
44 if (mpz_fits_ulong_p(val))
45 len = vstr_sc_conv_num_ulong(ui_buf, sizeof(ui_buf), mpz_get_ui(val),
46 "0123456789abcdef", nb);
47 else
48 {
49 len = mpz_sizeinbase(val, nb);
50 out_buf = buf = mpz_get_str(NULL, nb, val); /* dies on malloc error */
51
52 if (mpz_sgn(val) == -1) ++out_buf;
53 if (!out_buf[len - 1]) --len; /* see documentation for mpz_sizeinbase() */
54 }
55
56 ASSERT(strlen(out_buf) == len);
57
58 if (!vstr_sc_fmt_cb_beg(base, &pos, spec, &len, flags))
59 goto mem_fail;
60
61 if (spec->fmt_quote) /* add number including grouping */
62 ret = vstr_sc_add_grpbasenum_buf(base, pos, nb, out_buf, len);
63 else if (!EX_GMP_NUMS_USE_REFS || !buf) /* just add the number */
64 ret = vstr_add_buf(base, pos, out_buf, len);
65 else
66 { /* assumes mp_set_memory_functions() hasn't been called */
67 Vstr_ref *ref = vstr_ref_make_ptr(buf, vstr_ref_cb_free_ptr_ref);
68
69 if (!ref)
70 goto mem_fail;
71
72 ret = vstr_add_ref(base, pos, ref, out_buf - buf, len);
73
74 buf = NULL; /* memory is free'd when the reference is used up */
75
76 /* if !ret then this will free buf */
77 vstr_ref_del(ref);
78 }
79
80 if (!ret || !vstr_sc_fmt_cb_end(base, pos, spec, len))
81 goto mem_fail;
82
83 free(buf);
84
85 return (TRUE);
86
87 mem_fail:
88 free(buf);
89 return (FALSE);
90 }
91
ex_usr_dmpz_cb(Vstr_base * base,size_t pos,Vstr_fmt_spec * spec)92 static int ex_usr_dmpz_cb(Vstr_base *base, size_t pos, Vstr_fmt_spec *spec)
93 {
94 void *mpz = VSTR_FMT_CB_ARG_PTR(spec, 0);
95
96 return (ex__usr_mpz_cb(base, pos, spec, 'd', mpz));
97 }
98
ex_usr_ompz_cb(Vstr_base * base,size_t pos,Vstr_fmt_spec * spec)99 static int ex_usr_ompz_cb(Vstr_base *base, size_t pos, Vstr_fmt_spec *spec)
100 {
101 void *mpz = VSTR_FMT_CB_ARG_PTR(spec, 0);
102
103 return (ex__usr_mpz_cb(base, pos, spec, 'o', mpz));
104 }
105
ex_usr_xmpz_cb(Vstr_base * base,size_t pos,Vstr_fmt_spec * spec)106 static int ex_usr_xmpz_cb(Vstr_base *base, size_t pos, Vstr_fmt_spec *spec)
107 {
108 void *mpz = VSTR_FMT_CB_ARG_PTR(spec, 0);
109
110 return (ex__usr_mpz_cb(base, pos, spec, 'x', mpz));
111 }
112
113 /* Extra, do binary output... */
ex_usr_bmpz_cb(Vstr_base * base,size_t pos,Vstr_fmt_spec * spec)114 static int ex_usr_bmpz_cb(Vstr_base *base, size_t pos, Vstr_fmt_spec *spec)
115 {
116 void *mpz = VSTR_FMT_CB_ARG_PTR(spec, 0);
117
118 return (ex__usr_mpz_cb(base, pos, spec, 'b', mpz));
119 }
120
vmpz_num(unsigned int base,int val,unsigned int * ern,void * bignum)121 static void *vmpz_num(unsigned int base, int val,
122 unsigned int *ern, void *bignum)
123 {
124 *ern = 0;
125
126 mpz_mul_ui(bignum, bignum, base);
127
128 if (val > 0)
129 mpz_add_ui(bignum, bignum, val);
130 else
131 {
132 unsigned int uval = ++val;
133 uval = -uval;
134 ++uval;
135 mpz_sub_ui(bignum, bignum, uval);
136 }
137
138 return (bignum);
139 }
140
main(int argc,char * argv[])141 int main(int argc, char *argv[])
142 {
143 Vstr_base *s1 = ex_init(NULL);
144 mpz_t bignum;
145 const char *loc_num_name = NULL;
146 Vstr_ref *ref = NULL;
147 const unsigned int num_flags = (VSTR_FLAG_PARSE_NUM_SEP |
148 VSTR_FLAG_PARSE_NUM_SPACE);
149
150 if (argc < 2)
151 errx(EXIT_FAILURE, "No count specified");
152
153 vstr_cntl_conf(s1->conf, VSTR_CNTL_CONF_SET_FMT_CHAR_ESC, '$');
154 vstr_fmt_add(s1->conf, "<dMPZ:%p>", ex_usr_dmpz_cb,
155 VSTR_TYPE_FMT_PTR_VOID, VSTR_TYPE_FMT_END);
156 vstr_fmt_add(s1->conf, "<oMPZ:%p>", ex_usr_ompz_cb,
157 VSTR_TYPE_FMT_PTR_VOID, VSTR_TYPE_FMT_END);
158 vstr_fmt_add(s1->conf, "<xMPZ:%p>", ex_usr_xmpz_cb,
159 VSTR_TYPE_FMT_PTR_VOID, VSTR_TYPE_FMT_END);
160 vstr_fmt_add(s1->conf, "<bMPZ:%p>", ex_usr_bmpz_cb,
161 VSTR_TYPE_FMT_PTR_VOID, VSTR_TYPE_FMT_END);
162
163 setlocale(LC_ALL, "");
164 loc_num_name = setlocale(LC_NUMERIC, NULL);
165
166 if (!vstr_cntl_conf(s1->conf, VSTR_CNTL_CONF_SET_LOC_CSTR_AUTO_NAME_NUMERIC,
167 loc_num_name))
168 errx(EXIT_FAILURE, "Couldn't change numeric locale info");
169
170 mpz_init(bignum);
171
172 if (!vstr_add_cstr_ptr(s1, 0, argv[1]) ||
173 !vstr_parse_num(s1, 1, s1->len, num_flags, NULL, NULL, vmpz_num, bignum))
174 errno = ENOMEM, err(EXIT_FAILURE, "Couldn't init number");
175
176 vstr_del(s1, 1, s1->len);
177
178 #if (EX_GMP_NUMS_USE_TST)
179 {
180 mpz_abs(bignum, bignum);
181 mpz_neg(bignum, bignum);
182 do
183 {
184 mpz_neg(bignum, bignum);
185
186 vstr_add_fmt(s1, s1->len, "%%'20.40d = <$'20.40<dMPZ:%p>>\n",
187 (void *)bignum);
188 if (mpz_fits_slong_p(bignum))
189 vstr_add_fmt(s1, s1->len, "%%'20.40d = <%'20.40ld>\n",
190 mpz_get_si(bignum));
191 vstr_add_fmt(s1, s1->len, "%%'40.20d = <$'40.20<dMPZ:%p>>\n",
192 (void *)bignum);
193 if (mpz_fits_slong_p(bignum))
194 vstr_add_fmt(s1, s1->len, "%%'40.20d = <%'40.20ld>\n",
195 mpz_get_si(bignum));
196 vstr_add_fmt(s1, s1->len, "%%'20.40o = <$'20.40<oMPZ:%p>>\n",
197 (void *)bignum);
198 if ((mpz_sgn(bignum) != -1) && mpz_fits_slong_p(bignum))
199 vstr_add_fmt(s1, s1->len, "%%'20.40o = <%'20.40lo>\n",
200 mpz_get_si(bignum));
201 vstr_add_fmt(s1, s1->len, "%%'40.20o = <$'40.20<oMPZ:%p>>\n",
202 (void *)bignum);
203 if ((mpz_sgn(bignum) != -1) && mpz_fits_slong_p(bignum))
204 vstr_add_fmt(s1, s1->len, "%%'40.20o = <%'40.20lo>\n",
205 mpz_get_si(bignum));
206 vstr_add_fmt(s1, s1->len, "%%'20.40x = <$'20.40<xMPZ:%p>>\n",
207 (void *)bignum);
208 if ((mpz_sgn(bignum) != -1) && mpz_fits_slong_p(bignum))
209 vstr_add_fmt(s1, s1->len, "%%'20.40x = <%'20.40lx>\n",
210 mpz_get_si(bignum));
211 vstr_add_fmt(s1, s1->len, "%%'40.20x = <$'40.20<xMPZ:%p>>\n",
212 (void *)bignum);
213 if ((mpz_sgn(bignum) != -1) && mpz_fits_slong_p(bignum))
214 vstr_add_fmt(s1, s1->len, "%%'40.20x = <%'40.20lx>\n",
215 mpz_get_si(bignum));
216
217 vstr_add_fmt(s1, s1->len, "%%#'40d = <$#'40<dMPZ:%p>>\n",
218 (void *)bignum);
219 if (mpz_fits_slong_p(bignum))
220 vstr_add_fmt(s1, s1->len, "%%#'40d = <%#'40ld>\n",
221 mpz_get_si(bignum));
222 vstr_add_fmt(s1, s1->len, "%%#'-40d = <$#'-40<dMPZ:%p>>\n",
223 (void *)bignum);
224 if (mpz_fits_slong_p(bignum))
225 vstr_add_fmt(s1, s1->len, "%%#'-40d = <%#'-40ld>\n",
226 mpz_get_si(bignum));
227 vstr_add_fmt(s1, s1->len, "%%#'40o = <$#'40<oMPZ:%p>>\n",
228 (void *)bignum);
229 if ((mpz_sgn(bignum) != -1) && mpz_fits_slong_p(bignum))
230 vstr_add_fmt(s1, s1->len, "%%#'40o = <%#'40lo>\n",
231 mpz_get_si(bignum));
232 vstr_add_fmt(s1, s1->len, "%%#'-40o = <$#'-40<oMPZ:%p>>\n",
233 (void *)bignum);
234 if ((mpz_sgn(bignum) != -1) && mpz_fits_slong_p(bignum))
235 vstr_add_fmt(s1, s1->len, "%%#'-40o = <%#'-40lo>\n",
236 mpz_get_si(bignum));
237 vstr_add_fmt(s1, s1->len, "%%#'40x = <$#'40<xMPZ:%p>>\n",
238 (void *)bignum);
239 if ((mpz_sgn(bignum) != -1) && mpz_fits_slong_p(bignum))
240 vstr_add_fmt(s1, s1->len, "%%#'40x = <%#'40lx>\n",
241 mpz_get_si(bignum));
242 vstr_add_fmt(s1, s1->len, "%%#'-40x = <$#'-40<xMPZ:%p>>\n",
243 (void *)bignum);
244 if ((mpz_sgn(bignum) != -1) && mpz_fits_slong_p(bignum))
245 vstr_add_fmt(s1, s1->len, "%%#'-40x = <%#'-40lx>\n",
246 mpz_get_si(bignum));
247
248 vstr_add_fmt(s1, s1->len, "%%#'b = <$#'<bMPZ:%p>>\n",
249 (void *)bignum);
250
251 vstr_add_rep_chr(s1, s1->len, '-', 79);
252 vstr_add_rep_chr(s1, s1->len, '\n', 1);
253
254 } while (mpz_sgn(bignum) > 0);
255 mpz_abs(bignum, bignum);
256 }
257 #endif
258
259 if (!(ref = vstr_ref_make_strdup("_")))
260 errno = ENOMEM, err(EXIT_FAILURE, "Ref seperator");
261
262 if (!vstr_cntl_conf(NULL, VSTR_CNTL_CONF_SET_LOC_REF_THOU_SEP, 2, ref, 1) ||
263 !vstr_cntl_conf(NULL, VSTR_CNTL_CONF_SET_LOC_REF_THOU_SEP, 8, ref, 1) ||
264 !vstr_cntl_conf(NULL, VSTR_CNTL_CONF_SET_LOC_REF_THOU_SEP, 16, ref, 1) ||
265 FALSE)
266 errno = ENOMEM, err(EXIT_FAILURE, "Add seperator");
267 vstr_ref_del(ref);
268
269 if (!(ref = vstr_ref_make_strdup("\4")))
270 errno = ENOMEM, err(EXIT_FAILURE, "Ref grouping");
271
272 if (!vstr_cntl_conf(NULL, VSTR_CNTL_CONF_SET_LOC_REF_THOU_GRP, 2, ref) ||
273 !vstr_cntl_conf(NULL, VSTR_CNTL_CONF_SET_LOC_REF_THOU_GRP, 16, ref) ||
274 FALSE)
275 errno = ENOMEM, err(EXIT_FAILURE, "Add grouping");
276 vstr_ref_del(ref);
277
278 vstr_add_fmt(s1, s1->len, " Input: %s\n", argv[1]);
279 vstr_add_fmt(s1, s1->len, " %%#'x = $#'<xMPZ:%p>\n",
280 (void *)bignum);
281 vstr_add_fmt(s1, s1->len, " %%#'d = $#'<dMPZ:%p>\n",
282 (void *)bignum);
283 vstr_add_fmt(s1, s1->len, " %%#'o = $#'<oMPZ:%p>\n",
284 (void *)bignum);
285 vstr_add_fmt(s1, s1->len, " %%#'b = $#'<bMPZ:%p>\n",
286 (void *)bignum);
287
288 if (s1->conf->malloc_bad)
289 errno = ENOMEM, err(EXIT_FAILURE, "Add string data");
290
291 io_put_all(s1, STDOUT_FILENO);
292
293 exit (ex_exit(s1, NULL));
294 }
295