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;
 | |
| }
 | |
| 
 | |
| /****************************************************************************/
 | 
