aboutsummaryrefslogblamecommitdiff
path: root/src/common/libc/printf.c
blob: 8ec78d7de0f87f46bb3882fdd86e12c4a78d6518 (plain) (tree)
1
2
3
4
5
6
7
                   
                    
                  
 
                         

                          









                                                                
 















                                                                       


                             



















                                                                                        
 
                 


                                             

                                    

                                  
                                             

                                         





                                                              











                                                                                 

                                                     

                                 
                                               
                                                     













                                                                                        
                                         









                                                                                    
                                 
                                                      


                                                                    






                                                                  




                                                                                      
















                                                                                        
                                 

                                                     
                                 









                                                                                        
                                 


                                                   





                                           
                   
 

                                   
#include <stdarg.h>
#include <stdbool.h>
#include <ctype.h>

#include <kogata/debug.h>
#include <kogata/printf.h>

int snprintf(char * buff, size_t len, const char *format, ...) {
	va_list ap;

	va_start(ap, format);
	len = vsnprintf(buff, len, format, ap);
	va_end(ap);

	return len;
}


struct vsnprintf_s {
	char* buff;
	size_t len;
	size_t i;
};
int vsnprintf_putc_fun(int c, void* p) {
	struct vsnprintf_s *s = (struct vsnprintf_s*)p;
	if (s->i < s->len - 1) {
		s->buff[s->i] = c;
		s->i++;
		return 1;
	} else {
		return 0;
	}
}
int vsnprintf(char *buff, size_t len, const char* format, va_list arg){
	if (!buff || !format)
		return -1;

	struct vsnprintf_s s;
	s.buff = buff;
	s.len = len;
	s.i = 0;

	size_t ret = vcprintf(vsnprintf_putc_fun, &s, format, arg);
	ASSERT(ret == s.i);
	
	buff[s.i] = 0;
	return s.i;
}

int vcprintf(int (*putc_fun)(int c, void* p), void* p, const char* format, va_list ap) {
	if (!format) return -1;

	int ret = 0;
#define PUTCHAR(thechar) { int _tmp = putc_fun(thechar, p);\
						   if (_tmp == 1) ret++; \
						   else if (_tmp == 0) return ret; \
						   else if (_tmp < 0) return _tmp; }

	size_t i;
	for(i = 0; format[i] != '\0' ; i++) {
		if (format[i] == '%') {
			i++;
			int na = -1;
			int nb = -1;
			int u = 0;
			int l = 0;
			bool dotspec = false;
			bool spec = true;
			while (spec) {
				if (format[i] == 'l') {
					l++;
					i++;
				} else if (format[i] == 'u') {
					u = 1;
					i++;
				} else if (isdigit(format[i])) {
					if (dotspec) {
						if (nb == -1) nb = 0;
						nb = nb * 10 + (format[i] - '0');
					} else {
						if (na == -1) na = 0;
						na = na * 10 + (format[i] - '0');
					}
					i++;
				} else if (format[i] == '.') {
					dotspec = true;
					i++;
				} else {
					spec = false;
				}
			}
			if (format[i] == '%') {
					PUTCHAR('%');
			} else if (format[i] == 'i' || format[i] == 'd') {
				if (u) {
					// TODO
				} else {
					long long int integer;
					if (l == 0) integer = va_arg(ap, int);
					if (l == 1) integer = va_arg(ap, long int);
					if (l == 2) integer = va_arg(ap, long long int);

					int cpt2 = 0;
					char buff_int[32];

					if (integer<0) {
						PUTCHAR('-');
					}

					do {
						int m10 = integer%10;
						m10 = (m10 < 0)? -m10:m10;
						buff_int[cpt2++] = (char)('0'+ m10);
						integer = integer/10;
					} while(integer != 0);

					for(cpt2 = cpt2 - 1; cpt2 >= 0; cpt2--)
						PUTCHAR(buff_int[cpt2]);
				}
			} else if (format[i] == 'c') {
					 int value = va_arg(ap,int);
					 PUTCHAR((char)value);
					 break;
			} else if (format[i] == 's') {
				 char *string = va_arg(ap,char *);
				 if (!string)
					 string = "(null)";
				 for(; *string != '\0' ; string++)
					 PUTCHAR(*string);
			} else if (format[i] == 'x') {
				unsigned long long int hexa;
				if (l == 0) hexa = va_arg(ap, unsigned int);
				if (l == 1) hexa = va_arg(ap, unsigned long int);
				if (l == 2) hexa = va_arg(ap, unsigned long long int);

				int had_nonzero = 0;
				for(int j = 0; j < 8; j++) {
					unsigned int nb = (unsigned int)(hexa << (j*4));
					nb = (nb >> 28) & 0xf;
					// Skip the leading zeros
					if (nb == 0) {
						if (had_nonzero) {
							PUTCHAR('0');
						}
					} else {
						had_nonzero = 1;
						if (nb < 10) {
							PUTCHAR('0'+nb);
						} else {
							PUTCHAR('a'+(nb-10));
						}
					}
				}
				if (!had_nonzero) {
					PUTCHAR('0');
				}
			} else if (format[i] == 'p') {
				unsigned int hexa = va_arg(ap,int);
				for (int j = 0; j < 8; j++) {
					unsigned int nb = (unsigned int)(hexa << (j*4));
					nb = (nb >> 28) & 0xf;
					if (nb < 10) {
						PUTCHAR('0'+nb);
					} else {
						PUTCHAR('a'+(nb-10));
					}
				}
			} else {
				PUTCHAR('%');
				PUTCHAR(format[i]);
			}
		} else {
			PUTCHAR(format[i]);
		}
	}

	return ret;
}

/* vim: set ts=4 sw=4 tw=0 noet :*/