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