This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Upgrade to CPAN-1.88_54.
[perl5.git] / uts / sprintf_wrap.c
CommitLineData
c89df6bf
JH
1#include <stdlib.h>
2#include <stdio.h>
3#include <assert.h>
4#include <string.h>
5
6char *UTS_sprintf_wrap();
7char *do_efmt();
8char *do_gfmt();
9char *Fill();
10
11/* main(argc, argv)
12 * char **argv;
13 * {
14 * double d;
15 * char *Fmt, *Ret;
16 * char obuf[200];
17 *
18 * assert(argc > 2);
19 * Fmt = argv[1];
20 * d = strtod(argv[2], (char **)0);
21 *
22 * putchar('{');
23 * printf(Fmt, d);
24 * printf("}\n");
25 *
26 * Ret = UTS_sprintf_wrap(obuf, Fmt, d);
27 * assert(Ret == obuf);
28 *
29 * printf("{%s}\n", obuf);
30 * }
31 */
32
33char *
34UTS_sprintf_wrap(obuf, fmt, d,
35 a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15)
36char *obuf, *fmt;
37double d;
38{
39 int fmtlen, Width=0, Precision=6, Alt=0, Plus=0, Minus=0,
40 Zero = 0;
41 int FmtChar, BaseFmt = 0;
42 char *f = fmt, *AfterWidth = 0, *AfterPrecision = 0;
43 char *Dot;
44
45 if(*f++ != '%') {
46 return
47sprintf(obuf, fmt, d, a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15);
48 }
49 fmtlen = strlen(fmt);
50 FmtChar = fmt[fmtlen - 1];
51 switch(FmtChar) {
52 case 'f':
53 case 'F':
54 return
55sprintf(obuf, fmt, d, a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15);
56 case 'e':
57 case 'E':
58 BaseFmt = 'e';
59 goto BaseFmt_IsSet;
60 case 'g':
61 case 'G':
62 BaseFmt = 'g';
63BaseFmt_IsSet:
64 if(*f == '#') { Alt = 1; ++f; } /* Always has '.' */
65 if(*f == '+') { Plus = 1; ++f; } /* Force explicit sign */
66 if(*f == '-') { Minus = 1; ++f; } /* Left justify */
67 if(*f == '0') { Zero = 1; ++f;} /* Fill using 0s*/
68 if(Dot = strchr(f, '.')) {
69 Precision = strtol(Dot+1, &AfterPrecision, 0);
70 }
71 if(!Dot || (Dot && Dot > f)) { /* Next char=='.' => no width*/
72 Width = strtol(f, &AfterWidth, 0);
73 }
74 if(Dot) { f = AfterPrecision; }
75 else if(AfterWidth) { f = AfterWidth; }
76 if(*f != FmtChar) goto regular_sprintf;
77 /* It doesn't look like a f.p. sprintf call */
78 /* from Perl_sv_vcatpvfn */
79
80 if(BaseFmt == 'e') {
81 return do_efmt(d, obuf, Width, Precision, Alt,
82 Plus, Minus, Zero, FmtChar == 'E');
83 } else {
84 return do_gfmt(d, obuf, Width, Precision, Alt,
85 Plus, Minus, Zero, FmtChar == 'G');
86 }
87 default:
88regular_sprintf:
89 return
90sprintf(obuf, fmt, d, a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15);
91 }
92}
93
94char *
95do_efmt(d, obuf, Width, Precision, Alt, Plus, Minus, Zero, UpperCase)
96char *obuf;
97double d;
98{
99 char *Ecvt;
100 char *ob;
101 int decpt, sign, E;
102 int len;
103 int AllZeroes = 0;
104
105 Ecvt = ecvt( d , Precision+1, &decpt, &sign);
106
107 /* fprintf(stderr, "decpt=%d, sign=%d\n", decpt, sign); */
108
109 len = strlen(Ecvt);
110 if(strspn(Ecvt, "0") == len) AllZeroes = 1;
111
112 ob = obuf;
113 if(sign) *ob++ = '-';
114 else if(Plus) *ob++ = '+';
115
116 *ob++ = Ecvt[0];
117
118 if(Precision > 0 || Alt) *ob++ = '.';
119 strcpy(ob, &Ecvt[1]);
120
121 ob += strlen(ob); /* ADVANCE TO END OF WHAT WE JUST ADDED */
122 *ob++ = UpperCase ? 'E' : 'e';
123
124 if(AllZeroes) E = 0;
125 else E = decpt - 1;
126
127 if(E < 0) { *ob++ = '-'; E = -E; }
128 else { *ob++ = '+'; }
129
130 sprintf(ob, "%.2d", E); /* Too much horsepower used here */
131
132 if(Width > strlen(obuf)) return Fill(obuf, Width, Minus, Zero);
133 else return obuf;
134}
135
136char *
137do_gfmt(d, obuf, Width, Precision, Alt, Plus, Minus, Zero, UpperCase)
138char *obuf;
139double d;
140{
141 char *Ecvt = gcvt(d, Precision ? Precision : 1, obuf);
142 int len = strlen(obuf);
143
144 /* gcvt fails (maybe give a warning? For now return empty string): */
145 if(!Ecvt) { *obuf = '\0'; return obuf; }
146
147 /* printf("Ecvt='%s'\n", Ecvt); */
148 if(Plus && (Ecvt[0] != '-')) {
149 memmove(obuf+1, obuf, len+1); /* "+1" to get '\0' at end */
150 obuf[0] = '+';
151 ++len;
152 }
153 if(Alt && !strchr(Ecvt, '.')) {
154 int LenUpTo_E = strcspn(obuf, "eE");
155 int E_etc_len = strlen(&obuf[LenUpTo_E]);
156 /* ABOVE: Will be 0 if there's no E/e because */
157 /* strcspn will return length of whole string */
158
159 if(E_etc_len)
160 memmove(obuf+LenUpTo_E+1, obuf+LenUpTo_E, E_etc_len);
161 obuf[LenUpTo_E] = '.';
162 obuf[LenUpTo_E + 1 + E_etc_len ] = '\0';
163 }
164 { char *E_loc;
165 if(UpperCase && (E_loc = strchr(obuf, 'e'))) { *E_loc = 'E'; }
166 }
167 if(Width > len)
168 return Fill(obuf, Width, Minus, Zero);
169 else
170 return obuf;
171}
172
173char *
174Fill(obuf, Width, LeftJustify, Zero)
175char *obuf;
176{
177 int W = strlen(obuf);
178 int diff = Width - W;
179 /* LeftJustify means there was a '-' flag, and in that case, */
180 /* printf man page (UTS4.4) says ignore '0' */
181 char FillChar = (Zero && !LeftJustify) ? '0' : ' ';
182 int i;
183 int LeftFill = ! LeftJustify;
184
185 if(Width <= W) return obuf;
186
187 if(LeftFill) {
188 memmove(obuf+diff, obuf, W+1); /* "+1" to get '\0' at end */
189 for(i=0 ; i < diff ; ++i) { obuf[i] = FillChar; }
190 } else {
191 for(i=W ; i < Width ; ++i)
192 obuf[i] = FillChar;
193 obuf[Width] = '\0';
194 }
195 return obuf;
196}