8b99ff589314551c3c6bfb5464c3762e1a85f4ca
[mes.git] / lib / stdio / vsnprintf.c
1 /* -*-comment-start: "//";comment-end:""-*-
2  * GNU Mes --- Maxwell Equations of Software
3  * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
4  *
5  * This file is part of GNU Mes.
6  *
7  * GNU Mes is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or (at
10  * your option) any later version.
11  *
12  * GNU Mes is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <mes/lib.h>
22 #include <ctype.h>
23 #include <stdarg.h>
24 #include <string.h>
25
26 int
27 vsnprintf (char *str, size_t size, char const *format, va_list ap)
28 {
29   char const *p = format;
30   int count = 0;
31   char c;
32   while (*p)
33     if (*p != '%')
34       {
35         c = *p++;
36         if (count < size)
37           *str++ = c;
38         count++;
39       }
40     else
41       {
42         p++;
43         c = *p;
44         int left_p = 0;
45         int precision = -1;
46         int width = -1;
47         if (c == '-')
48           {
49             left_p = 1;
50             c = *++p;
51           }
52         char pad = ' ';
53         if (c == '0')
54           {
55             pad = c;
56             c = *p++;
57           }
58         if (c >= '0' && c <= '9')
59           {
60             width = abtol (&p, 10);
61             c = *p;
62           }
63         else if (c == '*')
64           {
65             width = va_arg (ap, long);
66             c = *++p;
67           }
68         if (c == '.')
69           {
70             c = *++p;
71             if (c >= '0' && c <= '9')
72               {
73                 precision = abtol (&p, 10);
74                 c = *p;
75               }
76             else if (c == '*')
77               {
78                 precision = va_arg (ap, long);
79                 c = *++p;
80               }
81           }
82         if (c == 'l')
83           c = *++p;
84         if (c == 'l')
85           c = *++p;
86         if (c == 'l')
87           {
88             eputs ("vsnprintf: skipping second: l\n");
89             c = *++p;
90           }
91         switch (c)
92           {
93           case '%':
94             {
95               if (count < size)
96                 *str++ = *p;
97               count++;
98               break;
99             }
100           case 'c':
101             {
102               c = va_arg (ap, long);
103               if (count < size)
104                 *str++ = c;
105               count++;
106               break;
107             }
108           case 'd':
109           case 'i':
110           case 'o':
111           case 'u':
112           case 'x':
113           case 'X':
114             {
115               long d = va_arg (ap, long);
116               int base = c == 'o' ? 8 : c == 'x' || c == 'X' ? 16 : 10;
117               char *s = ntoab (d, base, c != 'u' && c != 'x' && c != 'X');
118               if (c == 'X')
119                 strupr (s);
120               int length = strlen (s);
121               if (precision == -1)
122                 precision = length;
123               if (!left_p)
124                 {
125                   while (width-- > precision)
126                     {
127                       if (count < size)
128                         *str++ = pad;
129                       count++;
130                     }
131                   while (precision > length)
132                     {
133                       if (count < size)
134                         *str++ = '0';
135                       precision--;
136                       width--;
137                       count++;
138                     }
139                 }
140               while (*s)
141                 {
142                   if (precision-- <= 0)
143                     break;
144                   width--;
145                   c = *s++;
146                   if (count < size)
147                     *str++ = c;
148                   count++;
149                 }
150               while (width > 0)
151                 {
152                   width--;
153                   if (count < size)
154                     *str++ = pad;
155                   count++;
156                 }
157               break;
158             }
159           case 's':
160             {
161               char *s = va_arg (ap, char *);
162               int length = s ? strlen (s) : 0;
163               if (precision == -1)
164                 precision = length;
165               if (!left_p)
166                 {
167                   while (width-- > precision)
168                     {
169                       if (count < size)
170                         *str++ = pad;
171                       count++;
172                     }
173                   while (width > length)
174                     {
175                       if (count < size)
176                         *str++ = ' ';
177                       precision--;
178                       width--;
179                       count++;
180                     }
181                 }
182               while (s && *s)
183                 {
184                   if (precision-- <= 0)
185                     break;
186                   width--;
187                   c = *s++;
188                   if (count < size)
189                     *str++ = c;
190                   count++;
191                 }
192               while (width > 0)
193                 {
194                   width--;
195                   if (count < size)
196                     *str++ = pad;
197                   count++;
198                 }
199               break;
200             }
201           case 'f':
202           case 'e':
203           case 'E':
204           case 'g':
205           case 'G':
206             {
207               double d = va_arg (ap, double);
208               char *s = dtoab (d, 10, 1);
209               if (c == 'E' || c == 'G')
210                 strupr (s);
211               int length = strlen (s);
212               if (precision == -1)
213                 precision = length;
214               if (!left_p)
215                 {
216                   while (width-- > precision)
217                     {
218                       if (count < size)
219                         *str++ = pad;
220                       count++;
221                     }
222                   while (precision > length)
223                     {
224                       if (count < size)
225                         *str++ = '0';
226                       precision--;
227                       width--;
228                       count++;
229                     }
230                 }
231               while (*s)
232                 {
233                   if (precision-- <= 0)
234                     break;
235                   width--;
236                   c = *s++;
237                   if (count < size)
238                     *str++ = c;
239                   count++;
240                 }
241               while (width > 0)
242                 {
243                   width--;
244                   if (count < size)
245                     *str++ = pad;
246                   count++;
247                 }
248               break;
249             }
250           case 'n':
251             {
252               int *n = va_arg (ap, int *);
253               *n = count;
254               break;
255             }
256           default:
257             {
258               eputs ("vsnprintf: not supported: %:");
259               eputc (c);
260               eputs ("\n");
261               p++;
262             }
263           }
264         p++;
265       }
266   va_end (ap);
267   if (count < size)
268     *str = 0;
269   return count;
270 }