This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Ho! Ho! Ho! Santa brings consting!
[perl5.git] / uts / sprintf_wrap.c
1 #include        <stdlib.h>
2 #include        <stdio.h>
3 #include        <assert.h>
4 #include        <string.h>
5
6 char    *UTS_sprintf_wrap();
7 char    *do_efmt();
8 char    *do_gfmt();
9 char    *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
33 char *
34 UTS_sprintf_wrap(obuf, fmt, d,
35         a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15)
36 char    *obuf, *fmt;
37 double  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
47 sprintf(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
55 sprintf(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';
63 BaseFmt_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:
88 regular_sprintf:
89                 return
90 sprintf(obuf, fmt, d, a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15);
91         }
92 }
93
94 char    *
95 do_efmt(d, obuf, Width, Precision, Alt, Plus, Minus, Zero, UpperCase)
96 char    *obuf;
97 double  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
136 char    *
137 do_gfmt(d, obuf, Width, Precision, Alt, Plus, Minus, Zero, UpperCase)
138 char    *obuf;
139 double  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
173 char *
174 Fill(obuf, Width, LeftJustify, Zero)
175 char    *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 }