rc

[fork] interactive rc shell
git clone https://hhvn.uk/rc
git clone git://hhvn.uk/rc
Log | Files | Refs | README | LICENSE

print.c (7831B)


      1 /* print.c -- formatted printing routines (Paul Haahr, 12/91) */
      2 
      3 #include "rc.h"
      4 #include <setjmp.h>
      5 
      6 #define	PRINT_ALLOCSIZE	((size_t)64)
      7 #define	SPRINT_BUFSIZ	((size_t)1024)
      8 
      9 #define	MAXCONV 256
     10 
     11 /*
     12  * conversion functions
     13  *	true return -> flag changes only, not a conversion
     14  */
     15 
     16 #define Flag(name, flag) \
     17 static bool name(Format *format, int ignore) { \
     18 	format->flags |= flag; \
     19 	return TRUE; \
     20 }
     21 
     22 Flag(uconv,	FMT_unsigned)
     23 Flag(rc_lconv,	FMT_long)
     24 
     25 #if HAVE_QUAD_T
     26 Flag(qconv,	FMT_quad)
     27 #endif
     28 
     29 Flag(altconv,	FMT_altform)
     30 Flag(leftconv,	FMT_leftside)
     31 Flag(dotconv,	FMT_f2set)
     32 
     33 static bool digitconv(Format *format, int c) {
     34 	if (format->flags & FMT_f2set)
     35 		format->f2 = 10 * format->f2 + c - '0';
     36 	else {
     37 		format->flags |= FMT_f1set;
     38 		format->f1 = 10 * format->f1 + c - '0';
     39 	}
     40 	return TRUE;
     41 }
     42 
     43 static bool zeroconv(Format *format, int ignore) {
     44 	if (format->flags & (FMT_f1set | FMT_f2set))
     45 		return digitconv(format, '0');
     46 	format->flags |= FMT_zeropad;
     47 	return TRUE;
     48 }
     49 
     50 static void pad(Format *format, size_t len, int c) {
     51 	while (len-- != 0)
     52 		fmtputc(format, c);
     53 }
     54 
     55 static bool sconv(Format *format, int ignore) {
     56 	char *s = va_arg(format->args, char *);
     57 	if ((format->flags & FMT_f1set) == 0)
     58 		fmtcat(format, s);
     59 	else {
     60 		size_t len = strlen(s), width = format->f1 - len;
     61 		if (format->flags & FMT_leftside) {
     62 			fmtappend(format, s, len);
     63 			pad(format, width, ' ');
     64 		} else {
     65 			pad(format, width, ' ');
     66 			fmtappend(format, s, len);
     67 		}
     68 	}
     69 	return FALSE;
     70 }
     71 
     72 static char *rc_utoa(unsigned long u, char *t, unsigned int radix, const char *digit) {
     73 	if (u >= radix) {
     74 		t = rc_utoa(u / radix, t, radix, digit);
     75 		u %= radix;
     76 	}
     77 	*t++ = digit[u];
     78 	return t;
     79 }
     80 
     81 static void intconv(Format *format, unsigned int radix, int upper, const char *altform) {
     82 	static const char * const table[] = {
     83 		"0123456789abcdefghijklmnopqrstuvwxyz",
     84 		"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
     85 	};
     86 	char padchar;
     87 	size_t len, pre, zeroes, padding, width;
     88 	long n, flags;
     89 	unsigned long u;
     90 	char number[64], prefix[20];
     91 
     92 	if (radix > 36)
     93 		return;
     94 
     95 	flags = format->flags;
     96 
     97 #if HAVE_QUAD_T
     98 	if (flags & FMT_quad)
     99 		n = va_arg(format->args, quad_t);
    100 	else
    101 #endif
    102 
    103 	if (flags & FMT_long)
    104 		n = va_arg(format->args, long);
    105 	else
    106 		n = va_arg(format->args, int);
    107 
    108 	pre = 0;
    109 	if ((flags & FMT_unsigned) || n >= 0)
    110 		u = n;
    111 	else {
    112 		prefix[pre++] = '-';
    113 		u = -n;
    114 	}
    115 
    116 	if (flags & FMT_altform)
    117 		while (*altform != '\0')
    118 			prefix[pre++] = *altform++;
    119 
    120 	len = rc_utoa(u, number, radix, table[upper]) - number;
    121 	if ((flags & FMT_f2set) && (size_t) format->f2 > len)
    122 		zeroes = format->f2 - len;
    123 	else
    124 		zeroes = 0;
    125 
    126 	width = pre + zeroes + len;
    127 	if ((flags & FMT_f1set) && (size_t) format->f1 > width) {
    128 		padding = format->f1 - width;
    129 	} else
    130 		padding = 0;
    131 
    132 	padchar = ' ';
    133 	if (padding > 0 && flags & FMT_zeropad) {
    134 		padchar = '0';
    135 		if ((flags & FMT_leftside) == 0) {
    136 			zeroes += padding;
    137 			padding = 0;
    138 		}
    139 	}
    140 
    141 
    142 	if ((flags & FMT_leftside) == 0)
    143 		pad(format, padding, padchar);
    144 	fmtappend(format, prefix, pre);
    145 	pad(format, zeroes, '0');
    146 	fmtappend(format, number, len);
    147 	if (flags & FMT_leftside)
    148 		pad(format, padding, padchar);
    149 }
    150 
    151 static bool cconv(Format *format, int ignore) {
    152 	fmtputc(format, va_arg(format->args, int));
    153 	return FALSE;
    154 }
    155 
    156 static bool dconv(Format *format, int ignore) {
    157 	intconv(format, 10, 0, "");
    158 	return FALSE;
    159 }
    160 
    161 static bool oconv(Format *format, int ignore) {
    162 	intconv(format, 8, 0, "0");
    163 	return FALSE;
    164 }
    165 
    166 static bool xconv(Format *format, int ignore) {
    167 	intconv(format, 16, 0, "0x");
    168 	return FALSE;
    169 }
    170 
    171 static bool pctconv(Format *format, int ignore) {
    172 	fmtputc(format, '%');
    173 	return FALSE;
    174 }
    175 
    176 static bool badconv(Format *ignore, int ign0re) {
    177 	panic("bad conversion character in printfmt");
    178 	/* NOTREACHED */
    179 	return FALSE; /* hush up gcc -Wall */
    180 }
    181 
    182 
    183 /*
    184  * conversion table management
    185  */
    186 
    187 static Conv fmttab[MAXCONV];
    188 
    189 static void inittab(void) {
    190 	int i;
    191 	for (i = 0; i < MAXCONV; i++)
    192 		fmttab[i] = badconv;
    193 
    194 	fmttab['s'] = sconv;
    195 	fmttab['c'] = cconv;
    196 	fmttab['d'] = dconv;
    197 	fmttab['o'] = oconv;
    198 	fmttab['x'] = xconv;
    199 	fmttab['%'] = pctconv;
    200 
    201 	fmttab['u'] = uconv;
    202 	fmttab['l'] = rc_lconv;
    203 	fmttab['#'] = altconv;
    204 	fmttab['-'] = leftconv;
    205 	fmttab['.'] = dotconv;
    206 
    207 #if HAVE_QUAD_T
    208 	fmttab['q'] = qconv;
    209 #endif
    210 
    211 	fmttab['0'] = zeroconv;
    212 	for (i = '1'; i <= '9'; i++)
    213 		fmttab[i] = digitconv;
    214 }
    215 
    216 extern bool (*fmtinstall(int c, bool (*f)(Format *, int)))(Format *, int) {
    217 /*Conv fmtinstall(int c, Conv f) {*/
    218 	Conv oldf;
    219 	if (fmttab[0] == NULL)
    220 		inittab();
    221 	c &= MAXCONV - 1;
    222 	oldf = fmttab[c];
    223 	if (f != NULL)
    224 		fmttab[c] = f;
    225 	return oldf;
    226 }
    227 
    228 
    229 /*
    230  * functions for inserting strings in the format buffer
    231  */
    232 
    233 extern void fmtappend(Format *format, const char *s, size_t len) {
    234 	while (format->buf + len > format->bufend) {
    235 		size_t split = format->bufend - format->buf;
    236 		memcpy(format->buf, s, split);
    237 		format->buf += split;
    238 		s += split;
    239 		len -= split;
    240 		(*format->grow)(format, len);
    241 	}
    242 	memcpy(format->buf, s, len);
    243 	format->buf += len;
    244 }
    245 
    246 extern void fmtcat(Format *format, const char *s) {
    247 	fmtappend(format, s, strlen(s));
    248 }
    249 
    250 /*
    251  * printfmt -- the driver routine
    252  */
    253 
    254 extern int printfmt(Format *format, const char *fmt) {
    255 	unsigned const char *s = (unsigned const char *) fmt;
    256 
    257 	if (fmttab[0] == NULL)
    258 		inittab();
    259 
    260 	for (;;) {
    261 		int c = *s++;
    262 		switch (c) {
    263 		case '%':
    264 			format->flags = format->f1 = format->f2 = 0;
    265 			do
    266 				c = *s++;
    267 			while ((*fmttab[c])(format, c));
    268 			break;
    269 		case '\0':
    270 			return format->buf - format->bufbegin + format->flushed;
    271 		default:
    272 			fmtputc(format, c);
    273 			break;
    274 		}
    275 	}
    276 }
    277 
    278 
    279 /*
    280  * the public entry points
    281  */
    282 
    283 extern int fmtprint(Format *format, const char *fmt,...) {
    284 	int n = -format->flushed;
    285 	va_list ap, saveargs;
    286 
    287 	va_start(ap, fmt);
    288 	va_copy(saveargs, format->args);
    289 	va_copy(format->args, ap);
    290 	n += printfmt(format, fmt);
    291 	va_end(format->args);
    292 	va_copy(format->args, saveargs);
    293 
    294 	return n + format->flushed;
    295 }
    296 
    297 static void fprint_flush(Format *format, size_t ignore) {
    298 	size_t n = format->buf - format->bufbegin;
    299 	char *buf = format->bufbegin;
    300 
    301 	format->flushed += n;
    302 	format->buf = format->bufbegin;
    303 	writeall(format->u.n, buf, n);
    304 }
    305 
    306 extern int fprint(int fd, const char *fmt,...) {
    307 	char buf[1024];
    308 	Format format;
    309 	va_list ap;
    310 
    311 	format.buf	= buf;
    312 	format.bufbegin	= buf;
    313 	format.bufend	= buf + sizeof buf;
    314 	format.grow	= fprint_flush;
    315 	format.flushed	= 0;
    316 	format.u.n	= fd;
    317 
    318 	va_start(ap, fmt);
    319 	va_copy(format.args, ap);
    320 	printfmt(&format, fmt);
    321 	va_end(format.args);
    322 
    323 	fprint_flush(&format, 0);
    324 	return format.flushed;
    325 }
    326 
    327 static void memprint_grow(Format *format, size_t more) {
    328 	char *buf;
    329 	size_t len = format->bufend - format->bufbegin + 1;
    330 	size_t used = format->buf - format->bufbegin;
    331 
    332 	len = (len >= more)
    333 		? len * 2
    334 		: ((len + more) + PRINT_ALLOCSIZE) &~ (PRINT_ALLOCSIZE - 1);
    335 	if (format->u.n)
    336 		buf = erealloc(format->bufbegin, len);
    337 	else {
    338 		buf = nalloc(len);
    339 		memcpy(buf, format->bufbegin, used);
    340 	}
    341 	format->buf = buf + used;
    342 	format->bufbegin = buf;
    343 	format->bufend = buf + len - 1;
    344 }
    345 
    346 static char *memprint(Format *format, const char *fmt, char *buf, size_t len) {
    347 	format->buf	 = buf;
    348 	format->bufbegin = buf;
    349 	format->bufend	 = buf + len - 1;
    350 	format->grow	 = memprint_grow;
    351 	format->flushed	 = 0;
    352 	printfmt(format, fmt);
    353 	*format->buf = '\0';
    354 	return format->bufbegin;
    355 }
    356 
    357 extern char *mprint(const char *fmt,...) {
    358 	Format format;
    359 	char *result;
    360 	va_list ap;
    361 
    362 	format.u.n = 1;
    363 	va_start(ap, fmt);
    364 	va_copy(format.args, ap);
    365 	result = memprint(&format, fmt, ealloc(PRINT_ALLOCSIZE), PRINT_ALLOCSIZE);
    366 	va_end(format.args);
    367 	return result;
    368 }
    369 
    370 extern char *nprint(const char *fmt,...) {
    371 	Format format;
    372 	char *result;
    373 	va_list ap;
    374 
    375 	format.u.n = 0;
    376 	va_start(ap, fmt);
    377 	va_copy(format.args, ap);
    378 	result = memprint(&format, fmt, nalloc(PRINT_ALLOCSIZE), PRINT_ALLOCSIZE);
    379 	va_end(format.args);
    380 	return result;
    381 }