diff --git a/Usermode/Libraries/crt0.o_src/crt0.asm b/Usermode/Libraries/crt0.o_src/crt0.asm
index b8f52703688dc10858ed719ae2257cdddf71a508..81400c5088b2ec5bf008a16771fbe8fd205122c0 100644
--- a/Usermode/Libraries/crt0.o_src/crt0.asm
+++ b/Usermode/Libraries/crt0.o_src/crt0.asm
@@ -15,5 +15,16 @@ _start:
 start:
 	call main
 	push eax
+
+	mov eax, [_crt0_exit_handler]
+	test eax, eax
+	jz .exit
+	call [eax]
+	
+.exit:
 	call _exit
 	jmp $	; This should never be reached
+[section .bss]
+[global _crt0_exit_handler]
+_crt0_exit_handler:
+	resd	1
diff --git a/Usermode/Libraries/libc.so_src/fileIO.c b/Usermode/Libraries/libc.so_src/fileIO.c
index 4117b02f89cc00cc052725115ac199d366d441d8..b588f2461e760c11cce74a2c511025e000690ad2 100644
--- a/Usermode/Libraries/libc.so_src/fileIO.c
+++ b/Usermode/Libraries/libc.so_src/fileIO.c
@@ -299,12 +299,18 @@ EXPORT int vsprintf(char * __s, const char *__format, va_list __args)
 EXPORT int vsnprintf(char *buf, size_t __maxlen, const char *format, va_list args)
 {
 	char	tmp[65];
-	 int	c, minSize;
+	 int	c, minSize, precision, len;
 	 int	pos = 0;
 	char	*p;
 	char	pad;
 	uint64_t	arg;
-	 int	bLongLong;
+	 int	bLongLong, bPadLeft;
+
+	void _addchar(char ch)
+	{
+		if(buf && pos < __maxlen)	buf[pos] = ch;
+		pos ++;
+	}
 
 	tmp[32] = '\0';
 	
@@ -312,34 +318,64 @@ EXPORT int vsnprintf(char *buf, size_t __maxlen, const char *format, va_list arg
 	{
 		// Non-control character
 		if (c != '%') {
-			if(buf && pos < __maxlen)	buf[pos] = c;
-			pos ++;
+			_addchar(c);
 			continue;
 		}
 		
 		// Control Character
 		c = *format++;
 		if(c == '%') {	// Literal %
-			if(buf && pos < __maxlen)	buf[pos] = '%';
-			pos ++;
+			_addchar('%');
 			continue;
 		}
 		
-		// Padding
+		bPadLeft = 0;
+		bLongLong = 0;
+		minSize = 0;
+		precision = -1;
+		pad = ' ';
+		
+		// Padding Character
 		if(c == '0') {
 			pad = '0';
 			c = *format++;
-		} else
-			pad = ' ';
-		minSize = 0;
-		if('1' <= c && c <= '9')
-		{
-			while('0' <= c && c <= '9')
+		}
+		// Padding length
+		if( c == '*' ) {
+			// Variable length
+			minSize = va_arg(args, size_t);
+			c = *format++;
+		}
+		else {
+			if('1' <= c && c <= '9')
 			{
-				minSize *= 10;
-				minSize += c - '0';
+				minSize = 0;
+				while('0' <= c && c <= '9')
+				{
+					minSize *= 10;
+					minSize += c - '0';
+					c = *format++;
+				}
+			}
+		}
+
+		// Precision
+		if(c == '.') {
+			c = *format++;
+			if(c == '*') {
+				precision = va_arg(args, size_t);
 				c = *format++;
 			}
+			else if('1' <= c && c <= '9')
+			{
+				precision = 0;
+				while('0' <= c && c <= '9')
+				{
+					precision *= 10;
+					precision += c - '0';
+					c = *format++;
+				}
+			}
 		}
 	
 		// Check for long long
@@ -351,7 +387,8 @@ EXPORT int vsnprintf(char *buf, size_t __maxlen, const char *format, va_list arg
 				bLongLong = 1;
 			}
 		}
-			
+		
+		// Just help things along later
 		p = tmp;
 		
 		// Get Type
@@ -363,6 +400,7 @@ EXPORT int vsnprintf(char *buf, size_t __maxlen, const char *format, va_list arg
 			if(bLongLong)	arg = va_arg(args, int64_t);
 			else			arg = va_arg(args, int32_t);
 			itoa(tmp, arg, 10, minSize, pad, 1);
+			precision = -1;
 			goto sprintf_puts;
 		
 		// Unsigned Integer
@@ -371,25 +409,24 @@ EXPORT int vsnprintf(char *buf, size_t __maxlen, const char *format, va_list arg
 			if(bLongLong)	arg = va_arg(args, uint64_t);
 			else			arg = va_arg(args, uint32_t);
 			itoa(tmp, arg, 10, minSize, pad, 0);
+			precision = -1;
 			goto sprintf_puts;
 		
 		// Pointer
 		case 'p':
-			if(buf && pos+2 < __maxlen) {
-				buf[pos] = '*';
-				buf[pos+1] = '0';
-				buf[pos+2] = 'x';
-			}
-			pos += 3;
+			_addchar('*');
+			_addchar('0');
+			_addchar('x');
 			arg = va_arg(args, uint32_t);
 			itoa(tmp, arg, 16, minSize, pad, 0);
+			precision = -1;
 			goto sprintf_puts;
-			// Fall through to hex
 		// Unsigned Hexadecimal
 		case 'x':
 			if(bLongLong)	arg = va_arg(args, uint64_t);
 			else			arg = va_arg(args, uint32_t);
 			itoa(tmp, arg, 16, minSize, pad, 0);
+			precision = -1;
 			goto sprintf_puts;
 		
 		// Unsigned Octal
@@ -397,6 +434,7 @@ EXPORT int vsnprintf(char *buf, size_t __maxlen, const char *format, va_list arg
 			if(bLongLong)	arg = va_arg(args, uint64_t);
 			else			arg = va_arg(args, uint32_t);
 			itoa(tmp, arg, 8, minSize, pad, 0);
+			precision = -1;
 			goto sprintf_puts;
 		
 		// Unsigned binary
@@ -404,6 +442,7 @@ EXPORT int vsnprintf(char *buf, size_t __maxlen, const char *format, va_list arg
 			if(bLongLong)	arg = va_arg(args, uint64_t);
 			else			arg = va_arg(args, uint32_t);
 			itoa(tmp, arg, 2, minSize, pad, 0);
+			precision = -1;
 			goto sprintf_puts;
 
 		// String
@@ -413,28 +452,28 @@ EXPORT int vsnprintf(char *buf, size_t __maxlen, const char *format, va_list arg
 		sprintf_puts:
 			if(!p)	p = "(null)";
 			//_SysDebug("vsnprintf: p = '%s'", p);
-			if(buf) {
-				while(*p) {
-					if(pos < __maxlen)	buf[pos] = *p;
-					pos ++;	p ++;
-				}
-			}
-			else {
-				while(*p) {
-					pos++; p++;
-				}
+			if(precision >= 0)
+				len = strnlen(p, precision);
+			else
+				len = strlen(p);
+			if(bPadLeft)	while(minSize > len++)	_addchar(pad);
+			while( *p ) {
+				if(precision >= 0 && precision -- == 0)
+					break;
+				_addchar(*p++);
 			}
+			if(!bPadLeft)	while(minSize > len++)	_addchar(pad);
 			break;
 
 		// Unknown, just treat it as a character
 		default:
 			arg = va_arg(args, uint32_t);
-			if(buf && pos < __maxlen)	buf[pos] = arg;
-			pos ++;
+			_addchar(arg);
 			break;
 		}
-    }
-	if(buf && pos < __maxlen)	buf[pos] = '\0';
+	}
+	_addchar('\0');
+	pos --;
 	
 	//_SysDebug("vsnprintf: buf = '%s'", buf);
 	
diff --git a/Usermode/Libraries/libc.so_src/stdlib.c b/Usermode/Libraries/libc.so_src/stdlib.c
index 94d9d1dc3505ec5dbc243688b91c4be3d4dc3f85..191f5261bd7d588e5e711670941b59fe39ccf682 100644
--- a/Usermode/Libraries/libc.so_src/stdlib.c
+++ b/Usermode/Libraries/libc.so_src/stdlib.c
@@ -13,6 +13,8 @@
 #define _stdout	1
 #define _stdin	0
 
+extern void	*_crt0_exit_handler;
+
 // === PROTOTYPES ===
 EXPORT int	atoi(const char *str);
 EXPORT void	exit(int status);
@@ -24,6 +26,8 @@ void	(*g_stdlib_exithandler)(void);
 void atexit(void (*__func)(void))
 {
 	g_stdlib_exithandler = __func;
+	// TODO: Replace with meta-function to allow multiple atexit() handlers
+	_crt0_exit_handler = __func;	
 }
 
 /**
diff --git a/Usermode/Libraries/libc.so_src/string.c b/Usermode/Libraries/libc.so_src/string.c
index 607039eddb970ff61693caf95b907a8802c86b58..1c052366dfc984a5560a3adacfe75c870c11591e 100644
--- a/Usermode/Libraries/libc.so_src/string.c
+++ b/Usermode/Libraries/libc.so_src/string.c
@@ -77,17 +77,27 @@ EXPORT char *strcat(char *dst, const char *src)
 }
 
 /**
- * \fn EXPORT int strlen(const char *str)
  * \brief Get the length of a string
  */
-EXPORT int strlen(const char *str)
+EXPORT size_t strlen(const char *str)
 {
-	int retval;
-	for(retval = 0; *str != '\0'; str++)
-		retval++;
+	size_t	retval;
+	for(retval = 0; *str != '\0'; str++, retval++);
 	return retval;
 }
 
+/**
+ * \brief Get the length of a string, with a maximum of \a maxlen
+ * 
+ * Gets the length of a string (excluding the terminating \0 byte)
+ */
+EXPORT size_t strnlen(const char *str, size_t maxlen)
+{
+	size_t	len;
+	for( len = 0; maxlen -- && *str; str ++, len ++ );
+	return len;
+}
+
 /**
  * \fn EXPORT char *strdup(const char *str)
  * \brief Duplicate a string using heap memory
diff --git a/Usermode/include/string.h b/Usermode/include/string.h
index 00f748dd0685cf2b193126673ce829d9a3658d34..611dacbb1d4a139851df398073e82b4a5d14c243 100644
--- a/Usermode/include/string.h
+++ b/Usermode/include/string.h
@@ -8,7 +8,8 @@
 #include <stddef.h>
 
 // Strings
-extern int	strlen(const char *string);
+extern size_t	strlen(const char *string);
+extern size_t	strnlen(const char *string, size_t maxlen);
 extern int	strcmp(const char *str1, const char *str2);
 extern int	strncmp(const char *str1, const char *str2, size_t len);
 extern char	*strcpy(char *dst, const char *src);