summaryrefslogtreecommitdiff
path: root/user/printf.c
diff options
context:
space:
mode:
Diffstat (limited to 'user/printf.c')
-rw-r--r--user/printf.c95
1 files changed, 95 insertions, 0 deletions
diff --git a/user/printf.c b/user/printf.c
new file mode 100644
index 0000000..0c6b34b
--- /dev/null
+++ b/user/printf.c
@@ -0,0 +1,95 @@
+#include "kernel/types.h"
+#include "kernel/stat.h"
+#include "user/user.h"
+
+#include <stdarg.h>
+
+static char digits[] = "0123456789ABCDEF";
+
+static void
+putc(int fd, char c)
+{
+ write(fd, &c, 1);
+}
+
+static void
+printint(int fd, int xx, int base, int sgn)
+{
+ char buf[16];
+ int i, neg;
+ uint x;
+
+ neg = 0;
+ if(sgn && xx < 0){
+ neg = 1;
+ x = -xx;
+ } else {
+ x = xx;
+ }
+
+ i = 0;
+ do{
+ buf[i++] = digits[x % base];
+ }while((x /= base) != 0);
+ if(neg)
+ buf[i++] = '-';
+
+ while(--i >= 0)
+ putc(fd, buf[i]);
+}
+
+static void
+printptr(int fd, uint64 x) {
+ int i;
+ putc(fd, '0');
+ putc(fd, 'x');
+ for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4)
+ putc(fd, digits[x >> (sizeof(uint64) * 8 - 4)]);
+}
+
+// Print to the given fd. Only understands %d, %x, %p, %s.
+void
+printf(int fd, const char *fmt, ...)
+{
+ va_list ap;
+ char *s;
+ int c, i, state;
+
+ va_start(ap, fmt);
+ state = 0;
+ for(i = 0; fmt[i]; i++){
+ c = fmt[i] & 0xff;
+ if(state == 0){
+ if(c == '%'){
+ state = '%';
+ } else {
+ putc(fd, c);
+ }
+ } else if(state == '%'){
+ if(c == 'd'){
+ printint(fd, va_arg(ap, int), 10, 1);
+ } else if(c == 'x') {
+ printint(fd, va_arg(ap, int), 16, 0);
+ } else if(c == 'p') {
+ printptr(fd, va_arg(ap, uint64));
+ } else if(c == 's'){
+ s = va_arg(ap, char*);
+ if(s == 0)
+ s = "(null)";
+ while(*s != 0){
+ putc(fd, *s);
+ s++;
+ }
+ } else if(c == 'c'){
+ putc(fd, va_arg(ap, uint));
+ } else if(c == '%'){
+ putc(fd, c);
+ } else {
+ // Unknown % sequence. Print it to draw attention.
+ putc(fd, '%');
+ putc(fd, c);
+ }
+ state = 0;
+ }
+ }
+}