]> git.sur5r.net Git - cc65/blob - libsrc/common/_printf.c
Remove #pragmas to switch to the RODATA segment since the compiler will do
[cc65] / libsrc / common / _printf.c
1 /*
2  * Helper function for the printf family.
3  */
4
5
6
7 #include <stdio.h>
8 #include <string.h>
9 #include <stdarg.h>
10 #include <stdlib.h>
11 #include <ctype.h>
12 #include "_printf.h"
13
14
15
16 /* Use static variables for locals */
17 #pragma staticlocals (1);
18
19
20
21 int _printf (struct outdesc* d, char* f, va_list ap)
22 {
23     outfunc fout;               /* Output function */
24     unsigned char type;         /* variable argument type */
25     char str [20];              /* string buffer */
26     char c;                     /* Current format char */
27     char leftjust;              /* left justify string */
28     char addsign;               /* always add + or - */
29     char addblank;              /* add blank instead of + */
30     char altform;               /* alternate form? */
31     char padchar;               /* pad with space or zeroes? */
32     char islong;                /* l modifier found */
33     unsigned arglen;            /* length of argument string */
34     unsigned prec;              /* Precision */
35     unsigned width;             /* Width of output field */
36     int  i;                     /* Integer value */
37     long l;                     /* Long value */
38     char* sptr;                 /* pointer to argument string */
39     register char* s;           /* work pointer to argument string */
40
41     /* Remember the format string in a register variable for shorter code */
42     register char* format = f;
43
44     /* Remember the output function in a local variable for speed and size */
45     fout = d->fout;
46
47     /* */
48     d->ccount = 0;
49     while (c = *format++) {
50
51         if (c != '%') {
52             fout (d, &c, 1);
53             continue;
54         }
55
56         /* %%? */
57         if (*format == '%') {
58             fout (d, format, 1);
59             ++format;
60             continue;
61         }
62
63         /* format is: %[flags][width][.precision][mod]type */
64
65         /* flags */
66         leftjust = addsign = addblank = altform = 0;
67         do {
68             switch (c = *format) {
69
70                 case '-':
71                     leftjust = 1;
72                     break;
73
74                 case '+':
75                     addsign = 1;
76                     break;
77
78                 case '#':
79                     altform = 1;
80                     break;
81
82                 case ' ':
83                     addblank = 1;
84                     break;
85
86                 default:
87                     goto flags_done;
88
89             }
90             ++format;
91         } while (1);
92 flags_done:
93
94         /* width */
95         padchar = ' ';
96         if (*format == '0') {
97             padchar = '0';
98             ++format;
99         }
100         if (*format == '*') {
101             width = va_arg (ap, int);
102             ++format;
103         } else {
104             width = 0;
105             while (isdigit (c = *format)) {
106                 width = width * 10 + (c - '0');
107                 ++format;
108             }
109         }
110
111         /* precision */
112         prec = 0;
113         if (*format == '.') {
114             ++format;
115             if (*format == '*') {
116                 prec = va_arg (ap, int);
117                 ++format;
118             } else {
119                 while (isdigit (c = *format)) {
120                     prec = prec * 10 + (c - '0');
121                     ++format;
122                 }
123             }
124         }
125
126         /* modifiers */
127         islong = 0;
128         while (strchr ("FNhlL", c = *format)) {
129             switch (c) {
130
131                 case 'l':
132                     islong = 1;
133                     break;
134
135             }
136             ++format;
137         }
138
139         /* Check the format specifier */
140         sptr = s = str;
141         type = *format++;
142         switch (type) {
143
144             case 'c':
145                 str [0] = va_arg (ap, char);
146                 str [1] = 0;
147                 break;
148
149             case 'd':
150             case 'i':
151                 if (addsign) {
152                     *s++ = '+';
153                 } else if (addblank) {
154                     *s++ = ' ';
155                 }
156                 if (islong) {
157                     ltoa (va_arg (ap, long), s, 10);
158                 } else {
159                     itoa (va_arg (ap, int), s, 10);
160                 }
161                 break;
162
163             case 'n':
164                 *va_arg (ap, int*) = d->ccount;
165                 continue;
166
167             case 'o':
168                 if (islong) {
169                     l = va_arg (ap, unsigned long);
170                     if (altform && (l || prec)) {
171                         *s++ = '0';
172                     }
173                     ultoa (l, s, 8);
174                 } else {
175                     i = va_arg (ap, unsigned);
176                     if (altform && (i || prec)) {
177                         *s++ = '0';
178                     }
179                     utoa (i, s, 8);
180                 }
181                 break;
182
183             case 's':
184                 sptr = va_arg (ap, char*);
185                 break;
186
187             case 'u':
188                 if (islong) {
189                     ultoa (va_arg (ap, unsigned long), str, 10);
190                 } else {
191                     utoa (va_arg (ap, unsigned), str, 10);
192                 }
193                 break;
194
195             case 'x':
196             case 'X':
197                 if (altform) {
198                     *s++ = '0';
199                     *s++ = 'X';
200                 }
201                 if (islong) {
202                     ultoa (va_arg (ap, unsigned long), s, 16);
203                 } else {
204                     utoa (va_arg (ap, unsigned), s, 16);
205                 }
206                 if (type == 'x') {
207                     strlower (str);
208                 }
209                 break;
210
211             default:
212                 /* Unknown type char - skip it */
213                 continue;
214
215         }
216
217         /* Do argument string formatting */
218         arglen = strlen (sptr);
219         if (prec && prec < arglen) {
220             arglen = prec;
221         }
222         if (width > arglen) {
223             width -= arglen;            /* padcount */
224         } else {
225             width = 0;
226         }
227
228         /* Do padding on the left side if needed */
229         if (!leftjust) {
230             /* argument right justified */
231             while (width) {
232                 fout (d, &padchar, 1);
233                 --width;
234             }
235         }
236
237         /* Output the argument string */
238         fout (d, sptr, arglen);
239
240         /* Output right padding bytes if needed */
241         if (leftjust) {
242             /* argument left justified */
243             while (width) {
244                 fout (d, &padchar, 1);
245                 --width;
246             }
247         }
248
249     }
250 }
251
252
253