summaryrefslogtreecommitdiff
path: root/lib/sprint.c
blob: 05ddfb50cefb39ba44eaf0aa0773dae542dcdb61 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/**
** @file	sprint.c
**
** @author	Numerous CSCI-452 classes
**
** @brief	C implementations of common library functions
*/

#ifndef SPRINT_SRC_INC
#define SPRINT_SRC_INC

#include <common.h>

#include <lib.h>

/**
** sprint(dst,fmt,...) - formatted output into a string buffer
**
** @param dst The string buffer
** @param fmt Format string
**
** The format string parameter is followed by zero or more additional
** parameters which are interpreted according to the format string.
**
** NOTE:  assumes the buffer is large enough to hold the result string
**
** NOTE:  relies heavily on the x86 parameter passing convention
** (parameters are pushed onto the stack in reverse order as
** 32-bit values).
*/
void sprint(char *dst, char *fmt, ...)
{
	int32_t *ap;
	char buf[12];
	char ch;
	char *str;
	int leftadjust;
	int width;
	int len;
	int padchar;

	/*
	** Get characters from the format string and process them
	**
	** We use the "old-school" method of handling variable numbers
	** of parameters.  We assume that parameters are passed on the
	** runtime stack in consecutive longwords; thus, if the first
	** parameter is at location 'x', the second is at 'x+4', the
	** third at 'x+8', etc.  We use a pointer to a 32-bit thing
	** to point to the next "thing", and interpret it according
	** to the format string.
	*/

	// get the pointer to the first "value" parameter
	ap = (int *)(&fmt) + 1;

	// iterate through the format string
	while ((ch = *fmt++) != '\0') {
		/*
		** Is it the start of a format code?
		*/
		if (ch == '%') {
			/*
			** Yes, get the padding and width options (if there).
			** Alignment must come at the beginning, then fill,
			** then width.
			*/
			leftadjust = 0;
			padchar = ' ';
			width = 0;
			ch = *fmt++;
			if (ch == '-') {
				leftadjust = 1;
				ch = *fmt++;
			}
			if (ch == '0') {
				padchar = '0';
				ch = *fmt++;
			}
			while (ch >= '0' && ch <= '9') {
				width *= 10;
				width += ch - '0';
				ch = *fmt++;
			}

			/*
			** What data type do we have?
			*/
			switch (ch) {
			case 'c': // characters are passed as 32-bit values
				ch = *ap++;
				buf[0] = ch;
				buf[1] = '\0';
				dst = padstr(dst, buf, 1, width, leftadjust, padchar);
				break;

			case 'd':
				len = cvtdec(buf, *ap++);
				dst = padstr(dst, buf, len, width, leftadjust, padchar);
				break;

			case 's':
				str = (char *)(*ap++);
				dst = padstr(dst, str, -1, width, leftadjust, padchar);
				break;

			case 'x':
				len = cvthex(buf, *ap++);
				dst = padstr(dst, buf, len, width, leftadjust, padchar);
				break;

			case 'o':
				len = cvtoct(buf, *ap++);
				dst = padstr(dst, buf, len, width, leftadjust, padchar);
				break;

			case 'u':
				len = cvtuns(buf, *ap++);
				dst = padstr(dst, buf, len, width, leftadjust, padchar);
				break;
			}
		} else {
			// no, it's just an ordinary character
			*dst++ = ch;
		}
	}

	// NUL-terminate the result
	*dst = '\0';
}

#endif