41147db8a4826e9de83676f145aecc86d2d995dc
[mes.git] / lib / stdio / vsprintf.c
1 /* -*-comment-start: "//";comment-end:""-*-
2  * GNU Mes --- Maxwell Equations of Software
3  * Copyright © 2017,2018 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 <stdarg.h>
22 #include <stdio.h>
23
24 int
25 vsprintf (char *str, char const* format, va_list ap)
26 {
27   char const *p = format;
28   int count = 0;
29   while (*p)
30     if (*p != '%')
31       {
32         *str++ = *p++;
33         count++;
34       }
35     else
36       {
37         p++;
38         char c = *p;
39         int left_p = 0;
40         int precision = -1;
41         int width = -1;
42         if (c == '-')
43           {
44             left_p = 1;
45             c = *++p;
46           }
47         char pad = ' ';
48         if (c == '0')
49           {
50             pad = c;
51             c = *p++;
52           }
53         if (c >= '0' && c <= '9')
54           {
55             width = abtol (&p, 10);
56             c = *p;
57           }
58         else if (c == '*')
59           {
60             width = va_arg (ap, long);
61             c = *++p;
62           }
63         if (c == '.')
64           {
65             c = *++p;
66             if (c >= '0' && c <= '9')
67               {
68                 precision = abtol (&p, 10);
69                 c = *p;
70               }
71             else if (c == '*')
72               {
73                 precision = va_arg (ap, long);
74                 c = *++p;
75               }
76           }
77         if (c == 'l')
78           c = *++p;
79         if (c == 'l')
80           c = *++p;
81         if (c == 'l')
82           {
83             eputs ("vfprintf: skipping second: l\n");
84             c = *++p;
85           }
86         switch (c)
87           {
88           case '%':
89             {
90               *str++ = *p;
91               count++;
92               break;
93             }
94           case 'c':
95             {
96               c = va_arg (ap, long);
97               *str++ = c;
98               count++;
99               break;
100             }
101           case 'd':
102           case 'i':
103           case 'o':
104           case 'u':
105           case 'x':
106           case 'X':
107             {
108               long d = va_arg (ap, long);
109               int base = c == 'o' ? 8
110                 : c == 'x' || c == 'X' ? 16
111                 : 10;
112               char const *s = ntoab (d, base, c != 'u' && c != 'x' && c != 'X');
113               if (c == 'X')
114                 strupr (s);
115               int length = strlen (s);
116               if (precision == -1)
117                 precision = length;
118               if (!left_p)
119                 {
120                   while (width-- > precision)
121                     {
122                       *str++ = pad;
123                       count++;
124                     }
125                   while (precision > length)
126                     {
127                       *str++ = '0';
128                       precision--;
129                       width--;
130                       count++;
131                     }
132                 }
133               while (*s)
134                 {
135                   if (precision-- <= 0)
136                     break;
137                   width--;
138                   *str++ = *s++;
139                   count++;
140                 }
141               while (width > 0)
142                 {
143                   width--;
144                   *str++ = pad;
145                   count++;
146                 }
147               break;
148             }
149           case 's':
150             {
151               char *s = va_arg (ap, char *);
152               int length = strlen (s);
153               if (precision == -1)
154                 precision = length;
155               if (!left_p)
156                 {
157                   while (width-- > precision)
158                     {
159                       *str++ = pad;
160                       count++;
161                     }
162                   while (width > length)
163                     {
164                       *str++ = ' ';
165                       precision--;
166                       width--;
167                       count++;
168                     }
169                 }
170               while (*s)
171                 {
172                   if (precision-- <= 0)
173                     break;
174                   width--;
175                   *str++ = *s++;
176                   count++;
177                 }
178               while (width > 0)
179                 {
180                   width--;
181                   *str++ = pad;
182                   count++;
183                 }
184               break;
185             }
186           case 'n':
187             {
188               int *n = va_arg (ap, int *);
189               *n = count;
190               break;
191             }
192           default:
193             {
194               eputs ("vsprintf: not supported: %:");
195               eputc (c);
196               eputs ("\n");
197               p++;
198             }
199           }
200         p++;
201       }
202   va_end (ap);
203   *str = 0;
204   return strlen (str);
205 }