scc

A fork of https://git.simple-cc.org/scc/ for Qute

git clone https://git.8pit.net/scc.git

  1#include <ctype.h>
  2#include <limits.h>
  3#include <stdarg.h>
  4#include <stdint.h>
  5#include <stdio.h>
  6#include <string.h>
  7#include <wchar.h>
  8
  9#include "../libc.h"
 10
 11#define MAXPREC    50
 12
 13#undef vfprintf
 14
 15enum {
 16	LONG     = 1 << 0,
 17	LLONG    = 1 << 1,
 18	SHORT    = 1 << 2,
 19	CHAR     = 1 << 3,
 20	SIZET    = 1 << 4,
 21	PTRDIFF  = 1 << 5,
 22	INTMAX   = 1 << 6,
 23	VOIDPTR  = 1 << 7,
 24	UNSIGNED = 1 << 8,
 25	ALTFORM  = 1 << 9,
 26};
 27
 28struct conv {
 29	int sign;
 30	size_t prec;
 31	char *digs;
 32	int base;
 33};
 34
 35static uintmax_t
 36getnum(va_list *va, int flags, int *sign)
 37{
 38	uintmax_t uval;
 39	intmax_t val;
 40
 41	if (flags & CHAR) {
 42		val = va_arg(*va, int);
 43		uval = (unsigned char) val;
 44	} else if (flags & SHORT) {
 45		val = va_arg(*va, int);
 46		uval = (unsigned short) val;
 47	} else if (flags & LONG) {
 48		val = va_arg(*va, long);
 49		uval = (unsigned long) val;
 50	} else if (flags & LLONG) {
 51		val = va_arg(*va, long long);
 52		uval = (unsigned long long) val;
 53	} else if (flags & SIZET) {
 54		val = 0;
 55		uval = va_arg(*va, size_t);
 56	} else if (flags & INTMAX) {
 57		val = va_arg(*va, intmax_t);
 58		uval = (uintmax_t) val;
 59	} else if (flags & VOIDPTR) {
 60		uval = (uintmax_t) va_arg(*va, void *);
 61	} else {
 62		val = va_arg(*va, int);
 63		uval = (unsigned) val;
 64	}
 65
 66	if ((flags & UNSIGNED) == 0 && val < 0) {
 67		*sign = '-';
 68		uval = -val;
 69	}
 70	return uval;
 71}
 72
 73static char *
 74numtostr(uintmax_t val, int flags, struct conv *conv, char *buf)
 75{
 76	char *buf0 = buf;
 77	int base = conv->base, prec = conv->prec;
 78	uintmax_t oval = val;
 79
 80	*buf = '\0';
 81	do {
 82		*--buf = conv->digs[val % base];
 83		val /= base;
 84	} while (val > 0);
 85
 86	while (buf0 - buf < prec)
 87		*--buf = '0';
 88
 89	if (flags & ALTFORM) {
 90		if (base == 8 && *buf != '0') {
 91			*--buf = '0';
 92		} else if (base == 16 && oval != 0) {
 93			*--buf = conv->digs[16];
 94			*--buf = '0';
 95		}
 96	}
 97	if (conv->sign)
 98		*--buf = conv->sign;
 99
100	return buf;
101}
102
103static void
104savecnt(va_list *va, int flags, size_t cnt)
105{
106	if (flags & CHAR)
107		*va_arg(*va, char*) = cnt;
108	else if (flags & SHORT)
109		*va_arg(*va, short*) = cnt;
110	else if (flags & LONG)
111		*va_arg(*va, long*) = cnt;
112	else if (flags & LLONG)
113		*va_arg(*va, long long*) = cnt;
114	else if (flags & SIZET)
115		*va_arg(*va, size_t*) = cnt;
116	else if (flags & INTMAX)
117		*va_arg(*va, intmax_t*) = cnt;
118	else
119		*va_arg(*va, int*) = cnt;
120}
121
122static size_t
123wstrout(wchar_t *ws, size_t len, int width, int fill, FILE *restrict fp)
124{
125	int left = 0, adjust, n;
126	size_t cnt = 0;
127	wchar_t wc;
128
129	if (width < 0) {
130		left = 1;
131		width = -width;
132	}
133
134	adjust = len < width ? width - len : 0;
135	if (left)
136		adjust = -adjust;
137
138	for ( ; adjust > 0; adjust--) {
139		_fputwc(fill, fp, &n);
140		cnt += n;
141	}
142
143	for ( ; len-- > 0 && (wc = *ws) != '\0'; ++ws) {
144		_fputwc(wc, fp, &n);
145		cnt += n;
146	}
147
148	for ( ; adjust < 0; adjust++) {
149		_fputwc(' ', fp, &n);
150		cnt += n;
151	}
152
153	return cnt;
154}
155
156static size_t
157strout(char *s, size_t len, int width, int fill, FILE *restrict fp)
158{
159	int left = 0, adjust, ch, prefix;
160	size_t cnt = 0;
161
162	if (width < 0) {
163		left = 1;
164		width = -width;
165	}
166
167	adjust = len < width ? width - len : 0;
168	if (left)
169		adjust = -adjust;
170
171	if (fill == '0') {
172		if (*s == '-' || *s == '+')
173			prefix = 1;
174		else if (*s == '0' && toupper(s[1]) == 'X')
175			prefix = 2;
176		else
177			prefix = 0;
178		while (prefix--) {
179			putc(*s++, fp);
180			++cnt;
181			--len;
182		}
183	}
184
185	for ( ; adjust > 0; adjust--) {
186		putc(fill, fp);
187		++cnt;
188	}
189
190	for ( ; len-- > 0 && (ch = *s) != '\0'; ++s) {
191		putc(ch, fp);
192		++cnt;
193	}
194
195	for ( ; adjust < 0; adjust++) {
196		putc(' ', fp);
197		++cnt;
198	}
199
200	return cnt;
201}
202
203int
204vfprintf(FILE *restrict fp, const char *restrict fmt, va_list va)
205{
206	int cnt, ch, n;
207	int flags, width, left, fill, prec;
208	size_t inc, len;
209	char *s;
210	wchar_t *ws;
211	struct conv conv;
212	char buf[MAXPREC+1];
213	wchar_t wbuf[2];
214	va_list va2;
215
216	va_copy(va2, va);
217	for (cnt = 0; ch = *fmt++; cnt += inc) {
218		if (ch != '%') {
219			putc(ch, fp);
220			inc = 1;
221			continue;
222		}
223
224		fill = ' ';
225		prec = left = flags = width =  0;
226		conv.prec = 0;
227		conv.base = 10;
228		conv.sign = '\0';
229		conv.digs = "0123456789ABCDEFX";
230
231flags:
232		switch (*fmt++) {
233		case ' ':
234			if (conv.sign == '\0')
235				conv.sign = ' ';
236			goto flags;
237		case '+':
238			conv.sign = '+';
239			goto flags;
240		case '#':
241			flags |= ALTFORM;
242			goto flags;
243		case '.':
244			if (*fmt == '*') {
245				fmt++;
246				n = va_arg(va2, int);
247			} else {
248				for (n = 0; isdigit(ch = *fmt); fmt++)
249					n = n * 10 + ch - '0';
250			}
251			if (n >= 0) {
252				conv.prec = n;
253				prec = 1;
254			}
255			goto flags;
256		case '*':
257			width = va_arg(va2, int);
258			goto flags;
259		case '-':
260			left = 1;
261			++fmt;
262		case '1':
263		case '2':
264		case '3':
265		case '4':
266		case '5':
267		case '6':
268		case '7':
269		case '8':
270		case '9':
271			--fmt;
272			for (n = 0; isdigit(ch = *fmt); ++fmt)
273				n = n * 10 + ch - '0';
274			if (left)
275				n = -n;
276			width = n;
277			goto flags;
278		case '0':
279			fill = '0';
280			goto flags;
281		case 'l':
282			flags += LONG;
283			goto flags;
284		case 'h':
285			flags += SHORT;
286			goto flags;
287		case '%':
288			ch = '%';
289			goto cout;
290		case 'c':
291			if (flags & LONG) {
292				wbuf[0] = va_arg(va2, wint_t);
293				wbuf[1] = L'\0';
294				ws = wbuf;
295				len = 1;
296				goto wstrout;
297			}
298			ch = va_arg(va2, int);
299		cout:
300			buf[0] = ch;
301			buf[1] = '\0';
302			s = buf;
303			len = 1;
304			goto strout;
305		case 'j':
306			flags |= INTMAX;
307			goto flags;
308		case 't':
309			flags |= PTRDIFF;
310			goto flags;
311		case 'z':
312			flags |= SIZET;
313			goto flags;
314		case 'u':
315			flags |= UNSIGNED;
316		case 'i':
317		case 'd':
318			conv.base = 10;
319			goto numeric;
320		case 'p':
321			flags |= VOIDPTR | ALTFORM;
322		case 'x':
323			conv.digs = "0123456789abcdefx";
324		case 'X':
325		numeric16:
326			conv.base = 16;
327			flags |= UNSIGNED;
328			goto numeric;
329		case 'o':
330			conv.base = 8;
331			flags |= UNSIGNED;
332		numeric:
333			if (conv.prec > MAXPREC)
334				conv.prec = MAXPREC;
335			s = numtostr(getnum(&va2, flags, &conv.sign),
336			             flags,
337			             &conv,
338			             &buf[MAXPREC]);
339			len = &buf[MAXPREC] - s;
340			goto strout;
341		case 'L':
342		case 'a':
343		case 'A':
344		case 'e':
345		case 'E':
346		case 'f':
347		case 'g':
348		case 'G':
349			/* TODO */
350			break;
351		case 's':
352			if (flags & LONG) {
353				ws = va_arg(va2, wchar_t *);
354				len = wcslen(ws);
355				if (prec && len > conv.prec)
356					len = conv.prec;
357				goto wstrout;
358			} else {
359				s = va_arg(va2, char *);
360				len = strlen(s);
361				if (prec && len > conv.prec)
362					len = conv.prec;
363				goto strout;
364			}
365		wstrout:
366			inc = wstrout(ws, len, width, fill, fp);
367			break;
368		strout:
369			inc = strout(s, len, width, fill, fp);
370			break;
371		case 'n':
372			savecnt(&va2, flags, cnt);
373			break;
374		case '\0':
375			goto out_loop;
376		}
377	}
378
379out_loop:
380	return (ferror(fp)) ? EOF : cnt;
381}