I decided to make my own version of snprintf
in C. I intentionally changed some things though. My version guarantees the buffer printed to will be null-terminated, and it returns the number of characters printed to the buffer, not the number that would have been printed if the buffer's size was not limited. And I only worried about some of the main formatting features, like %s
, %c
, %d
, %h
, and %H
.
I would love to know what I could do better in this and improve, or what aspects of it I did or did not implement well.
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
int INT_TO_STR_DIGITS_L[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
int INT_TO_STR_DIGITS_U[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
int int_to_str(int x, char *buf, size_t size, int base, int uppercase) {
int length = (int)ceil(log((double)x)/log((double)base));
int r, i = 0;
char c;
if (size < length) {
x /= (int)pow(base, (float)(length - size));
length = size;
}
do {
if (i >= size) break;
r = x % base;
if (uppercase) {
c = INT_TO_STR_DIGITS_U[r];
} else {
c = INT_TO_STR_DIGITS_L[r];
}
buf[length-i-1] = c;
x /= base;
i++;
} while (x != 0);
return i;
}
int my_snprintf(char *str, size_t max_size, const char *fmt, ...) {
va_list arg_list;
va_start(arg_list, fmt);
int chars_printed = 0;
char *start_str = str;
char c, *str_arg;
int num, len;
int uppercase = 0, base = 10;
for (int i = 0; fmt[i] != 0; i++) {
if (max_size - chars_printed <= 0) {
break;
} else if (fmt[i] == '%') {
i++;
switch (fmt[i]) {
case 'c':
c = va_arg(arg_list, int);
str[chars_printed++] = c;
break;
case '%':
str[chars_printed++] = '%';
break;
case 's':
str_arg = va_arg(arg_list, char *);
len = strnlen(str_arg, max_size - chars_printed);
strncpy(str+chars_printed, str_arg, len);
chars_printed += len;
break;
case 'H':
uppercase = 1;
case 'h':
base = 16;
case 'd':
num = va_arg(arg_list, int);
len = int_to_str(num, str+chars_printed, max_size - chars_printed, base, uppercase);
chars_printed += len;
break;
default:
printf("Invalid format.\n");
va_end(arg_list);
return -1;
}
} else {
str[chars_printed++] = fmt[i];
}
}
if (chars_printed == max_size) chars_printed--;
str[chars_printed] = 0;
va_end(arg_list);
return chars_printed;
}