231 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			231 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/* tinyprnt.c */
 | 
						|
 | 
						|
#if OSK
 | 
						|
#define sprintf Sprintf
 | 
						|
#endif
 | 
						|
 | 
						|
/* This is a limited version of sprintf().  It is useful for Minix-PC and
 | 
						|
 * Coherent-286 because those systems are both limited to 64k+64k and the
 | 
						|
 * standard sprintf() is just too damn big.
 | 
						|
 *
 | 
						|
 * It should also be useful for OS-9 because OS-9's sprintf() doesn't
 | 
						|
 * understand the true meaning of asterisks in a format string.  This one
 | 
						|
 * does.
 | 
						|
 */
 | 
						|
 | 
						|
/* Place-holders in format strings look like "%<pad><clip><type>".
 | 
						|
 *
 | 
						|
 * The <pad> adds space to the front (or, if negative, to the back) of the
 | 
						|
 * output value, to pad it to a given width.  If <pad> is absent, then 0 is
 | 
						|
 * assumed.  If <pad> is an asterisk, then the next argument is assumed to
 | 
						|
 * be an (int) which used as the pad width.
 | 
						|
 *
 | 
						|
 * The <clip> string can be absent, in which case no clipping is done.
 | 
						|
 * However, if it is present, then it should be either a "." followed by
 | 
						|
 * a number, or a "." followed by an asterisk.  The asterisk means that the
 | 
						|
 * next argument is an (int) which should be used as the pad width.  Clipping
 | 
						|
 * only affects strings; for other data types it is ignored.
 | 
						|
 *
 | 
						|
 * The <type> is one of "s" for strings, "c" for characters (really ints that
 | 
						|
 * are assumed to be legal char values), "d" for ints, "ld" for long ints, or
 | 
						|
 * "%" to output a percent sign.
 | 
						|
 */
 | 
						|
 | 
						|
/* NOTE: Variable argument lists are handled by direct stack-twiddling. Sorry! */
 | 
						|
 | 
						|
static void cvtnum(buf, num, base)
 | 
						|
	char		*buf;	/* where to store the number */
 | 
						|
	unsigned long	num;	/* the number to convert */
 | 
						|
	int		base;	/* either 8, 10, or 16 */
 | 
						|
{
 | 
						|
	static char	digits[] = "0123456789abcdef";
 | 
						|
	unsigned long	tmp;
 | 
						|
 | 
						|
	/* if the number is 0, then just stuff a "0" into the buffer */
 | 
						|
	if (num == 0L)
 | 
						|
	{
 | 
						|
		buf[0] = '0';
 | 
						|
		buf[1] = '\0';
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* use tmp to figure out how many digits we'll need */
 | 
						|
	for (tmp = num; tmp > 0; tmp /= base)
 | 
						|
	{
 | 
						|
		buf++;
 | 
						|
	}
 | 
						|
 | 
						|
	/* mark the spot that will be the end of the string */
 | 
						|
	*buf = '\0';
 | 
						|
 | 
						|
	/* generate all digits, as needed */
 | 
						|
	for (tmp = num; tmp > 0; tmp /= base)
 | 
						|
	{
 | 
						|
		*--buf = digits[tmp % base];
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
int sprintf(buf, fmt, argref)
 | 
						|
	char	*buf;	/* where to deposit the formatted output */
 | 
						|
	char	*fmt;	/* the format string */
 | 
						|
	int	argref;	/* the first argument is located at &argref */
 | 
						|
{
 | 
						|
	char	*argptr;/* pointer to next argument on the stack */
 | 
						|
	int	pad;	/* value of the pad string */
 | 
						|
	int	clip;	/* value of the clip string */
 | 
						|
	long	num;	/* a binary number being converted to ASCII digits */
 | 
						|
	long	digit;	/* used during conversion */
 | 
						|
	char	*src, *dst;
 | 
						|
 | 
						|
	/* make argptr point to the first argument after the format string */
 | 
						|
	argptr = (char *)&argref;
 | 
						|
 | 
						|
	/* loop through the whole format string */
 | 
						|
	while (*fmt)
 | 
						|
	{
 | 
						|
		/* if not part of a place-holder, then copy it literally */
 | 
						|
		if (*fmt != '%')
 | 
						|
		{
 | 
						|
			*buf++ = *fmt++;
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		/* found a place-holder!  Get <pad> value */
 | 
						|
		fmt++;
 | 
						|
		if ('*' == *fmt)
 | 
						|
		{
 | 
						|
			pad = *((int *)argptr)++;
 | 
						|
			fmt++;
 | 
						|
		}
 | 
						|
		else if (*fmt == '-' || (*fmt >= '0' && *fmt <= '9'))
 | 
						|
		{
 | 
						|
			pad = atol(fmt);
 | 
						|
			do
 | 
						|
			{
 | 
						|
				fmt++;
 | 
						|
			} while (*fmt >= '0' && *fmt <= '9');
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			pad = 0;
 | 
						|
		}
 | 
						|
 | 
						|
		/* get a <clip> value */
 | 
						|
		if (*fmt == '.')
 | 
						|
		{
 | 
						|
			fmt++;
 | 
						|
			if ('*' == *fmt)
 | 
						|
			{
 | 
						|
				clip = *((int *)argptr)++;
 | 
						|
				fmt++;
 | 
						|
			}
 | 
						|
			else if (*fmt >= '0' && *fmt <= '9')
 | 
						|
			{
 | 
						|
				clip = atol(fmt);
 | 
						|
				do
 | 
						|
				{
 | 
						|
					fmt++;
 | 
						|
				} while (*fmt >= '0' && *fmt <= '9');
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			clip = 0;
 | 
						|
		}
 | 
						|
 | 
						|
		/* handle <type>, possibly noticing <clip> */
 | 
						|
		switch (*fmt++)
 | 
						|
		{
 | 
						|
		  case 'c':
 | 
						|
			buf[0] = *((int *)argptr)++;
 | 
						|
			buf[1] = '\0';
 | 
						|
			break;
 | 
						|
 | 
						|
		  case 's':
 | 
						|
			src = *((char **)argptr)++;
 | 
						|
			if (!src)
 | 
						|
			{
 | 
						|
				src = "(null)";
 | 
						|
			}
 | 
						|
			if (clip)
 | 
						|
			{
 | 
						|
				strncpy(buf, src, clip);
 | 
						|
				buf[clip] = '\0';
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				strcpy(buf, src);
 | 
						|
			}
 | 
						|
			break;
 | 
						|
 | 
						|
		  case 'l':
 | 
						|
			fmt++; /* to skip the "d" in "%ld" */
 | 
						|
			num = *((long *)argptr)++;
 | 
						|
			dst = buf;
 | 
						|
			if (num < 0)
 | 
						|
			{
 | 
						|
				*dst++ = '-';
 | 
						|
				num = -num;
 | 
						|
			}
 | 
						|
			cvtnum(dst, num, 10);
 | 
						|
			break;
 | 
						|
 | 
						|
		  case 'x':
 | 
						|
			num = *((int *)argptr)++;
 | 
						|
			cvtnum(buf, num, 16);
 | 
						|
			break;
 | 
						|
 | 
						|
		  case 'd':
 | 
						|
			num = *((int *)argptr)++;
 | 
						|
			dst = buf;
 | 
						|
			if (num < 0)
 | 
						|
			{
 | 
						|
				*dst++ = '-';
 | 
						|
				num = -num;
 | 
						|
			}
 | 
						|
			cvtnum(dst, num, 10);
 | 
						|
			break;
 | 
						|
 | 
						|
		  default:
 | 
						|
			buf[0] = fmt[-1];
 | 
						|
			buf[1] = '\0';
 | 
						|
		}
 | 
						|
 | 
						|
		/* now fix the padding, if the value is too short */
 | 
						|
		clip = strlen(buf);
 | 
						|
		if (pad < 0)
 | 
						|
		{
 | 
						|
			/* add spaces after the value */
 | 
						|
			pad = -pad - clip;
 | 
						|
			for (buf += clip; pad > 0; pad--)
 | 
						|
			{
 | 
						|
				*buf++ = ' ';
 | 
						|
			}
 | 
						|
			*buf = '\0';
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			/* add spaces before the value */
 | 
						|
			pad -= clip;
 | 
						|
			if (pad > 0)
 | 
						|
			{
 | 
						|
				src = buf + clip;
 | 
						|
				dst = src + pad;
 | 
						|
				*dst = '\0';
 | 
						|
				while (src > buf)
 | 
						|
				{
 | 
						|
					*--dst = *--src;
 | 
						|
				}
 | 
						|
				while (dst > buf)
 | 
						|
				{
 | 
						|
					*--dst = ' ';
 | 
						|
				}
 | 
						|
			}
 | 
						|
			buf += strlen(buf);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* mark the end of the output string */
 | 
						|
	*buf = '\0';
 | 
						|
}
 |