472 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			472 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
#if ever
 | 
						|
static char sccsid[] = "@(#)printf.c	(U of Maryland) FLB 6-Jan-1987";
 | 
						|
static char RCSid[] = "@(#)$Header$";
 | 
						|
#endif
 | 
						|
 | 
						|
/*
 | 
						|
 * Printf - Duplicate the C library routine of the same name, but from
 | 
						|
 *	    the shell command level.
 | 
						|
 *
 | 
						|
 * Fred Blonder <fred@Mimsy.umd.edu>
 | 
						|
 *
 | 
						|
 * To Compile:
 | 
						|
 %	cc -s -O printf.c -o printf
 | 
						|
 *
 | 
						|
 * $Log$
 | 
						|
 * Revision 1.1  2005/04/21 14:55:31  beng
 | 
						|
 * Initial revision
 | 
						|
 *
 | 
						|
 * Revision 1.1.1.1  2005/04/20 13:33:30  beng
 | 
						|
 * Initial import of minix 2.0.4
 | 
						|
 *
 | 
						|
 * Revision 1.4  87/01/29  20:52:30  fred
 | 
						|
 * Re-installed backslash-notation conversion for string & char arguments.
 | 
						|
 * 
 | 
						|
 * Revision 1.3  87/01/29  20:44:23  fred
 | 
						|
 * Converted to portable algorithm.
 | 
						|
 * Added Roman format for integers.
 | 
						|
 * 	29-Jan-87  FLB
 | 
						|
 * 
 | 
						|
 * Revision 1.2  87/01/09  19:10:57  fred
 | 
						|
 * Fixed bug in argument-count error-checking.
 | 
						|
 * Changed backslash escapes within strings to correspond to ANSII C
 | 
						|
 * 	draft standard.  (9-Jan-87 FLB)
 | 
						|
 * 
 | 
						|
 */
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#define EX_OK		0
 | 
						|
#define EX_USAGE	1
 | 
						|
 | 
						|
int ctrl(char *s);
 | 
						|
 | 
						|
#define atoi(a)		strtoul((a), NULL, 0)
 | 
						|
 | 
						|
/****************************************************************************/
 | 
						|
 | 
						|
int main(int argc, char *argv[])
 | 
						|
{
 | 
						|
register char *cp, *conv_spec, **argp, **ep;
 | 
						|
char *ctor(int x);
 | 
						|
 | 
						|
if (argc < 2) {
 | 
						|
	fprintf(stderr,
 | 
						|
		"printf: Usage: printf <format-string> [ arg1 . . . ]\n");
 | 
						|
	exit(EX_USAGE);
 | 
						|
	}
 | 
						|
 | 
						|
argp = &argv[2];	/* Point at first arg (if any) beyond format string. */
 | 
						|
ep = &argv[argc];	/* Point beyond last arg. */
 | 
						|
 | 
						|
ctrl(argv[1]);	/* Change backslash notation to control chars in fmt string. */
 | 
						|
 | 
						|
/* Scan format string for conversion specifications, and do appropriate
 | 
						|
   conversion on the corresponding argument. */
 | 
						|
for (cp = argv[1]; *cp; cp++) {
 | 
						|
register int dynamic_count;
 | 
						|
 | 
						|
	/* Look for next conversion spec. */
 | 
						|
	while (*cp && *cp != '%') {
 | 
						|
		putchar(*cp++);
 | 
						|
		}
 | 
						|
 | 
						|
	if (!*cp)	/* End of format string */
 | 
						|
		break;
 | 
						|
		
 | 
						|
	dynamic_count = 0;	/* Begin counting dynamic field width specs. */
 | 
						|
	conv_spec = cp++;	/* Remember where this conversion begins. */
 | 
						|
 | 
						|
	for (;*cp; cp++) {	/* Scan until conversion character. */
 | 
						|
		char conv_buf[BUFSIZ];	/* Save conversion string here. */
 | 
						|
		register int conv_len;	/* Length of ``conv_buf''. */
 | 
						|
 | 
						|
		switch (*cp) {	/* Field-width spec.: Keep scanning. */
 | 
						|
			case '.': case '0': case '1': case '2': case '3':
 | 
						|
			case '4': case '5': case '6': case '7': case '8':
 | 
						|
			case '9':
 | 
						|
				continue;
 | 
						|
 | 
						|
			case '*':	/* Dynamic field-width spec */
 | 
						|
				dynamic_count++;
 | 
						|
				continue;
 | 
						|
 | 
						|
			case 's':	/* String */
 | 
						|
				if (&argp[dynamic_count] >= ep) {
 | 
						|
					fprintf(stderr,
 | 
						|
					"printf: Not enough args for format.\n"
 | 
						|
						);
 | 
						|
					exit(EX_USAGE);
 | 
						|
					}
 | 
						|
 | 
						|
				(void) strncpy(conv_buf, conv_spec,
 | 
						|
					conv_len = cp - conv_spec + 1);
 | 
						|
				conv_buf[conv_len] = '\0';
 | 
						|
 | 
						|
				switch (dynamic_count) {
 | 
						|
					case 0:
 | 
						|
						ctrl(*argp);
 | 
						|
						printf(conv_buf, *argp++);
 | 
						|
						break;
 | 
						|
 | 
						|
					case 1:
 | 
						|
						{
 | 
						|
						register int a1;
 | 
						|
 | 
						|
						a1 = atoi(*argp++);
 | 
						|
						ctrl(*argp);
 | 
						|
						printf(conv_buf, a1, *argp++);
 | 
						|
						}
 | 
						|
						break;
 | 
						|
 | 
						|
					case 2:
 | 
						|
						{
 | 
						|
						register int a1, a2;
 | 
						|
 | 
						|
						a1 = atoi(*argp++);
 | 
						|
						a2 = atoi(*argp++);
 | 
						|
						ctrl(*argp);
 | 
						|
						printf(conv_buf, a1, a2, *argp++);
 | 
						|
						}
 | 
						|
						break;
 | 
						|
 | 
						|
					}
 | 
						|
				goto out;
 | 
						|
 | 
						|
			case 'c':	/* Char */
 | 
						|
				if (&argp[dynamic_count] >= ep) {
 | 
						|
					fprintf(stderr,
 | 
						|
					"printf: Not enough args for format.\n"
 | 
						|
						);
 | 
						|
					exit(EX_USAGE);
 | 
						|
					}
 | 
						|
 | 
						|
				(void) strncpy(conv_buf, conv_spec,
 | 
						|
					conv_len = cp - conv_spec + 1);
 | 
						|
				conv_buf[conv_len] = '\0';
 | 
						|
 | 
						|
				switch (dynamic_count) {
 | 
						|
					case 0:
 | 
						|
						ctrl(*argp);
 | 
						|
						printf(conv_buf, **argp++);
 | 
						|
						break;
 | 
						|
 | 
						|
					case 1:
 | 
						|
						{
 | 
						|
						register int a1;
 | 
						|
 | 
						|
						a1 = atoi(*argp++);
 | 
						|
						ctrl(*argp);
 | 
						|
						printf(conv_buf, a1, **argp++);
 | 
						|
						}
 | 
						|
						break;
 | 
						|
 | 
						|
					case 2:
 | 
						|
						{
 | 
						|
						register int a1, a2;
 | 
						|
 | 
						|
						a1 = atoi(*argp++);
 | 
						|
						a2 = atoi(*argp++);
 | 
						|
						ctrl(*argp);
 | 
						|
						printf(conv_buf, a1, a2, **argp++);
 | 
						|
						}
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				goto out;
 | 
						|
 | 
						|
			case 'd':	/* Integer */
 | 
						|
			case 'o':
 | 
						|
			case 'x':
 | 
						|
			case 'X':
 | 
						|
			case 'u':
 | 
						|
				if (&argp[dynamic_count] >= ep) {
 | 
						|
					fprintf(stderr,
 | 
						|
					"printf: Not enough args for format.\n"
 | 
						|
						);
 | 
						|
					exit(EX_USAGE);
 | 
						|
					}
 | 
						|
 | 
						|
				(void) strncpy(conv_buf, conv_spec,
 | 
						|
					conv_len = cp - conv_spec + 1);
 | 
						|
				conv_buf[conv_len] = '\0';
 | 
						|
 | 
						|
				switch (dynamic_count) {
 | 
						|
					case 0:
 | 
						|
						printf(conv_buf, atoi(*argp++));
 | 
						|
						break;
 | 
						|
 | 
						|
					case 1:
 | 
						|
						{
 | 
						|
						register int a1;
 | 
						|
 | 
						|
						a1 = atoi(*argp++);
 | 
						|
						printf(conv_buf, a1, atoi(*argp++));
 | 
						|
						}
 | 
						|
						break;
 | 
						|
 | 
						|
					case 2:
 | 
						|
						{
 | 
						|
						register int a1, a2;
 | 
						|
 | 
						|
						a1 = atoi(*argp++);
 | 
						|
						a2 = atoi(*argp++);
 | 
						|
						printf(conv_buf, a1, a2, atoi(*argp++));
 | 
						|
						}
 | 
						|
						break;
 | 
						|
 | 
						|
					}
 | 
						|
				goto out;
 | 
						|
 | 
						|
			case 'f':	/* Real */
 | 
						|
			case 'e':
 | 
						|
			case 'g':
 | 
						|
				if (&argp[dynamic_count] >= ep) {
 | 
						|
					fprintf(stderr,
 | 
						|
					"printf: Not enough args for format.\n"
 | 
						|
						);
 | 
						|
					exit(EX_USAGE);
 | 
						|
					}
 | 
						|
 | 
						|
				(void) strncpy(conv_buf, conv_spec,
 | 
						|
					conv_len = cp - conv_spec + 1);
 | 
						|
				conv_buf[conv_len] = '\0';
 | 
						|
 | 
						|
				switch (dynamic_count) {
 | 
						|
					case 0:
 | 
						|
						printf(conv_buf, atof(*argp++));
 | 
						|
						break;
 | 
						|
 | 
						|
					case 1:
 | 
						|
						{
 | 
						|
						register int a1;
 | 
						|
 | 
						|
						a1 = atoi(*argp++);
 | 
						|
						printf(conv_buf, a1, atof(*argp++));
 | 
						|
						}
 | 
						|
						break;
 | 
						|
 | 
						|
					case 2:
 | 
						|
						{
 | 
						|
						register int a1, a2;
 | 
						|
 | 
						|
						a1 = atoi(*argp++);
 | 
						|
						a2 = atoi(*argp++);
 | 
						|
						printf(conv_buf, a1, a2, atof(*argp++));
 | 
						|
						}
 | 
						|
						break;
 | 
						|
 | 
						|
					}
 | 
						|
				goto out;
 | 
						|
 | 
						|
			case 'r':	/* Roman (Well, why not?) */
 | 
						|
				if (&argp[dynamic_count] >= ep) {
 | 
						|
					fprintf(stderr,
 | 
						|
					"printf: Not enough args for format.\n"
 | 
						|
						);
 | 
						|
					exit(EX_USAGE);
 | 
						|
					}
 | 
						|
 | 
						|
				(void) strncpy(conv_buf, conv_spec,
 | 
						|
					conv_len = cp - conv_spec + 1);
 | 
						|
				conv_buf[conv_len] = '\0';
 | 
						|
				conv_buf[conv_len - 1] = 's';
 | 
						|
 | 
						|
				switch (dynamic_count) {
 | 
						|
					case 0:
 | 
						|
						printf(conv_buf,
 | 
						|
							ctor(atoi(*argp++)));
 | 
						|
						break;
 | 
						|
 | 
						|
					case 1:
 | 
						|
						{
 | 
						|
						register int a1;
 | 
						|
 | 
						|
						a1 = atoi(*argp++);
 | 
						|
						printf(conv_buf, a1,
 | 
						|
							ctor(atoi(*argp++)));
 | 
						|
						}
 | 
						|
						break;
 | 
						|
 | 
						|
					case 2:
 | 
						|
						{
 | 
						|
						register int a1, a2;
 | 
						|
 | 
						|
						a1 = atoi(*argp++);
 | 
						|
						a2 = atoi(*argp++);
 | 
						|
						printf(conv_buf, a1, a2,
 | 
						|
							ctor(atoi(*argp++)));
 | 
						|
						}
 | 
						|
						break;
 | 
						|
 | 
						|
					}
 | 
						|
				goto out;
 | 
						|
 | 
						|
			case '%':	/* Boring */
 | 
						|
				putchar('%');
 | 
						|
				break;
 | 
						|
 | 
						|
			default:	/* Probably an error, but let user
 | 
						|
					   have his way. */
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	out: ;
 | 
						|
	}
 | 
						|
 | 
						|
exit(EX_OK);
 | 
						|
}
 | 
						|
 | 
						|
/****************************************************************************/
 | 
						|
 | 
						|
/* Convert backslash notation to control characters, in place. */
 | 
						|
 | 
						|
int ctrl(char *s)
 | 
						|
{
 | 
						|
register char *op;
 | 
						|
static int val;
 | 
						|
 | 
						|
for (op = s; *s; s++)
 | 
						|
	if (*s == '\\')
 | 
						|
		switch (*++s) {
 | 
						|
			case '\0':	/* End-of-string: user goofed */
 | 
						|
				goto out;
 | 
						|
 | 
						|
			case '\\':	/* Backslash */
 | 
						|
				*op++ = '\\';
 | 
						|
				break;
 | 
						|
 | 
						|
			case 'n':	/* newline */
 | 
						|
				*op++ = '\n';
 | 
						|
				break;
 | 
						|
 | 
						|
			case 't':	/* horizontal tab */
 | 
						|
				*op++ = '\t';
 | 
						|
				break;
 | 
						|
 | 
						|
			case 'r':	/* carriage-return */
 | 
						|
				*op++ = '\r';
 | 
						|
				break;
 | 
						|
 | 
						|
			case 'f':	/* form-feed */
 | 
						|
				*op++ = '\f';
 | 
						|
				break;
 | 
						|
 | 
						|
			case 'b':	/* backspace */
 | 
						|
				*op++ = '\b';
 | 
						|
				break;
 | 
						|
 | 
						|
			case 'v':	/* vertical tab */
 | 
						|
				*op++ = '\13';
 | 
						|
				break;
 | 
						|
 | 
						|
			case 'a':	/* WARNING! DANGER! DANGER! DANGER! */
 | 
						|
				*op++ = '\7';
 | 
						|
				break;
 | 
						|
 | 
						|
			case '0': case '1': case '2': case '3':
 | 
						|
			case '4': case '5': case '6': case '7':
 | 
						|
				{	/* octal constant */
 | 
						|
				register int digits;
 | 
						|
 | 
						|
				val = 0;
 | 
						|
				(void) sscanf(s, "%3o", &val);
 | 
						|
				*op++ = val;
 | 
						|
				for (digits = 3; s[1] &&
 | 
						|
					strchr("01234567", s[1])
 | 
						|
					&& --digits > 0;
 | 
						|
						s++);
 | 
						|
				}
 | 
						|
				break;
 | 
						|
 | 
						|
			case 'x':	/* hex constant */
 | 
						|
			case 'X':
 | 
						|
				s++;
 | 
						|
				{
 | 
						|
				register int digits;
 | 
						|
 | 
						|
				val = 0;
 | 
						|
				(void) sscanf(s, "%3x", &val);
 | 
						|
				*op++ = val;
 | 
						|
				for (digits = 3; *s && s[1] &&
 | 
						|
					strchr("0123456789abcdefABCDEF",
 | 
						|
									s[1])
 | 
						|
					&& --digits > 0;
 | 
						|
						s++);
 | 
						|
				}
 | 
						|
				break;
 | 
						|
 | 
						|
			}
 | 
						|
	else
 | 
						|
		*op++ = *s;
 | 
						|
 | 
						|
out:
 | 
						|
 | 
						|
*op = '\0';
 | 
						|
}
 | 
						|
 | 
						|
/****************************************************************************/
 | 
						|
 | 
						|
/* Convert integer to Roman Numerals. (Have have you survived without it?) */
 | 
						|
 | 
						|
struct roman {
 | 
						|
	unsigned r_mag;
 | 
						|
	char r_units, r_fives;
 | 
						|
	} roman[] = {
 | 
						|
		{ 1000, 'M', '\0', },
 | 
						|
		{  100, 'C', 'D',  },
 | 
						|
		{   10, 'X', 'L',  },
 | 
						|
		{    1, 'I', 'V',  },
 | 
						|
		};
 | 
						|
 | 
						|
char *ctor(int x)
 | 
						|
{
 | 
						|
register struct roman *mp;
 | 
						|
static char buf[BUFSIZ];
 | 
						|
register char *cp = buf;
 | 
						|
 | 
						|
/* I've never actually seen a roman numeral with a minus-sign.
 | 
						|
   Probably ought to print out some appropriate latin phrase instead. */
 | 
						|
if (x < 0) {
 | 
						|
	*cp++ = '-';
 | 
						|
	x = -x;
 | 
						|
	}
 | 
						|
 | 
						|
for (mp = roman; x; mp++) {
 | 
						|
	register unsigned units;
 | 
						|
 | 
						|
	units = x / mp->r_mag;
 | 
						|
	x = x % mp->r_mag;
 | 
						|
 | 
						|
	if (cp > &buf[BUFSIZ-2])
 | 
						|
		return "???";
 | 
						|
 | 
						|
	if (units == 9 && mp > roman) {	/* Do inverse notation: Eg: ``IX''. */
 | 
						|
		*cp++ = mp->r_units;
 | 
						|
		*cp++ = mp[-1].r_units;
 | 
						|
		}
 | 
						|
	else if (units == 4 && mp->r_fives) {
 | 
						|
		/* Inverse notation for half-decades: Eg: ``IV'' */
 | 
						|
		*cp++ = mp->r_units;
 | 
						|
		*cp++ = mp->r_fives;
 | 
						|
		}
 | 
						|
	else {	/* Additive notation */
 | 
						|
		if (units >= 5 && mp->r_fives) {
 | 
						|
			*cp++ = mp->r_fives;
 | 
						|
			units -= 5;
 | 
						|
			}
 | 
						|
		while (units--) {
 | 
						|
			*cp++ = mp->r_units;
 | 
						|
			if (cp > &buf[BUFSIZ-5])
 | 
						|
				return "???";
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
*cp = '\0';
 | 
						|
 | 
						|
return buf;
 | 
						|
}
 | 
						|
 | 
						|
/****************************************************************************/
 |