Newer
Older
rtlibc / src / iprintf.c
/* Copyright (C) Thornwave Labs Inc - All Rights Reserved
 * Unauthorized copying of this file, via any medium is strictly prohibited
 * Proprietary and confidential
 * Written by Razvan Turiac <razvan.turiac@thornwave.com>
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <../src/stdio_internal.h>


static const char l_flags[] = "-+ #0";
static const char l_length_chars[] = "hljzt";
static const char l_fs_chars[] = "duxXocs";


//print %u, %x or %X
static int32_t fsh_integer(rtprintf_output_handler_t output_handler, void *user, uint32_t val, const char *charset)
{
	int32_t rv = 0;
	
	char str[12];		//will fit a 32 bit number
	char* const str_end = str + sizeof(str);
	const uint32_t base = strlen(charset);

	char *ptr = str_end;
	if (val)
	{
		while(val)
		{
			*--ptr = charset[val % base];
			val /= base;
		}
	}
	else
		*--ptr = '0';

	OUTPUT_CHARS(user, ptr, str_end - ptr);

	return rv;
}



//str - string to parse
//spec - specifier found
//index - the index fo the data to be printed (before the real data we might have the width or/and precision passed as arguments)
static inline const char* parse_format_specifier(const char *str, char *spec, uint32_t *index)
{
	const char *ptr = str + 1;		//skip over the '%' character
	*index = 0;
	
	//parse flag
	while(*ptr && strchr(l_flags, *ptr))
		ptr++;

	//parse width
	if (*ptr == 0)
		return str;
	else if (*ptr == '*')
	{
		(*index)++;
		ptr++;
	}
	else
		while (*ptr && strchr("0123456789", *ptr))
			ptr++;

	//parse precision
	if (*ptr == '.')
	{
		ptr++;

		if (*ptr == 0)
			return str;		
		else if (*ptr == '*')
		{
			(*index)++;
			ptr++;
		}
		else
			while (*ptr && strchr("0123456789", *ptr))
				ptr++;
	}
	
	//parse length
	if (*ptr && strchr(l_length_chars, *ptr))
		ptr++;
		
	//parse type
	if (*ptr && strchr(l_fs_chars, *ptr))
	{
		*spec = *ptr;
		ptr++;
	}
	else
		return str;

	return ptr;
}



int32_t rtiprintf(rtprintf_output_handler_t output_handler, void *user, const char *format, va_list args)
{
	int32_t rv = 0;

	while(*format)
	{
		const char *specifier;
		ptrdiff_t offset = 0;

		do
		{
			specifier = strchr(format + offset, '%');
			offset += 2;
		}while(specifier && (specifier[1] == '%'));
			
	
		if (specifier)		//% char found
		{
			//print the string up to the % char
			const size_t output_size = specifier - format;
			if (output_size)
				OUTPUT_CHARS(user, format, output_size);

			//process the specifier
			char spec = 0;
			uint32_t index;
			const char *new_pos = parse_format_specifier(specifier, &spec, &index);

			if (new_pos != specifier)
			{
				while(index--)
					va_arg(args, uint32_t);		//skip width and/or precision specified as arguments
					
				//we have a valid specifier
				switch(spec)
				{
					case 'c':
					{
						const char val = va_arg(args, uint32_t);
						OUTPUT_CHARS(user, &val, 1);
					}break;

					case 'd':
					{
						int32_t val_signed = va_arg(args, int32_t);
						if (val_signed < 0)
						{
							OUTPUT_CHARS(user, "-", 1);
							val_signed = -val_signed;
						}
						const int32_t rv_temp = fsh_integer(output_handler, user, val_signed, "0123456789");
						if (rv_temp < 0)
							return rv_temp;
						else
							rv += rv_temp;
					}break;

					case 'u':
					{
						const int32_t rv_temp = fsh_integer(output_handler, user, va_arg(args, uint32_t), "0123456789");
						if (rv_temp < 0)
							return rv_temp;
						else
							rv += rv_temp;
					}break;
					
					case 'x':
					{
						const int32_t rv_temp = fsh_integer(output_handler, user, va_arg(args, uint32_t), "0123456789abcdef");
						if (rv_temp < 0)
							return rv_temp;
						else
							rv += rv_temp;
					}break;					
					
					case 'X':
					{
						const int32_t rv_temp = fsh_integer(output_handler, user, va_arg(args, uint32_t), "0123456789ABCDEF");
						if (rv_temp < 0)
							return rv_temp;
						else
							rv += rv_temp;
					}break;										

					case 's':
					{
						const char *str = va_arg(args, const char*);
						const char *ptr = str;
						while(*ptr)
							ptr++;
						OUTPUT_CHARS(user, str, ptr - str);
					}break;									
					
					default: break;

				}

			 	format = new_pos;			
			}
			else
			{
				//invalid specifier so print it
				OUTPUT_CHARS(user, specifier, 1);
				format = specifier + 1;					
			}
		}
		else
		{
			//no % char found
			//print the remainder of the string
			const char* str = format;
			while(*format)
				format++;

			OUTPUT_CHARS(user, str, format - str);
		}
	}

	return rv;
}