Skip to content

Instantly share code, notes, and snippets.

@ibeauregard
Last active January 28, 2021 03:42
Show Gist options
  • Save ibeauregard/deb1e70299abe33b5b1a6bfdad9726dd to your computer and use it in GitHub Desktop.
Save ibeauregard/deb1e70299abe33b5b1a6bfdad9726dd to your computer and use it in GitHub Desktop.
printf (simple, partial implementation)
#include <stdarg.h>
#include <unistd.h>
#include <stdio.h>
#define CONVERSION_TRIGGER '%'
#define MINUS_SIGN '-'
#define DIGITS "0123456789abcdef"
#define OCTAL 8
#define DECIMAL 10
#define HEXADECIMAL 16
#define HEXA_PREFIX "0x"
#define NULL_STRING_PLACEHOLDER "(null)"
#define FLUSH_TRIGGER '\n'
#ifndef STRUCT_OUTPUT_BUFFER
#define STRUCT_OUTPUT_BUFFER
typedef struct s_output_buffer
{
unsigned short index;
unsigned int count;
char array[BUFSIZ];
} output_buffer;
#endif
void flush(output_buffer* buffer)
{
write(STDOUT_FILENO, buffer->array, buffer->index);
buffer->index = 0;
}
void flush_if_full(output_buffer* buffer)
{
if (buffer->index == BUFSIZ)
{
flush(buffer);
}
}
void put(char c, output_buffer* buffer)
{
buffer->array[buffer->index++] = c;
if (c == FLUSH_TRIGGER)
{
return flush(buffer);
}
flush_if_full(buffer);
}
void print_char(char c, output_buffer* buffer)
{
put(c, buffer);
buffer->count++;
}
void print_string(char* s, output_buffer* buffer)
{
if (!s)
{
return print_string(NULL_STRING_PLACEHOLDER, buffer);
}
while(*s)
{
print_char(*s++, buffer);
}
}
void print_non_zero_unsigned_long(unsigned long u, unsigned char base, output_buffer* buffer)
{
if (u == 0)
{
return;
}
print_non_zero_unsigned_long(u / base, base, buffer);
print_char(DIGITS[u % base], buffer);
}
void print_unsigned_long(unsigned long u, unsigned char base, output_buffer* buffer)
{
if (u == 0)
{
return print_char('0', buffer);
}
print_non_zero_unsigned_long(u, base, buffer);
}
void c_specifier(char c, output_buffer* buffer)
{
print_char(c, buffer);
}
void d_specifier(int d, output_buffer* buffer)
{
if (d < 0)
{
print_char(MINUS_SIGN, buffer);
d = -d;
}
print_unsigned_long(d, DECIMAL, buffer);
}
void o_specifier(unsigned int u, output_buffer* buffer)
{
print_unsigned_long(u, OCTAL, buffer);
}
void p_specifier(void* p, output_buffer* buffer)
{
print_string(HEXA_PREFIX, buffer);
print_unsigned_long((unsigned long) p, HEXADECIMAL, buffer);
}
void s_specifier(char* s, output_buffer* buffer)
{
print_string(s, buffer);
}
void u_specifier(unsigned int u, output_buffer* buffer)
{
print_unsigned_long(u, DECIMAL, buffer);
}
void x_specifier(unsigned int u, output_buffer* buffer)
{
print_unsigned_long(u, HEXADECIMAL, buffer);
}
void handle_conversion(char specifier, va_list* args, output_buffer* buffer)
{
switch(specifier)
{
case 'd':
return d_specifier(va_arg(*args, int), buffer);
case 'o':
return o_specifier(va_arg(*args, unsigned int), buffer);
case 'u':
return u_specifier(va_arg(*args, unsigned int), buffer);
case 'x':
return x_specifier(va_arg(*args, unsigned int), buffer);
case 'c':
return c_specifier((char) va_arg(*args, int), buffer);
case 's':
return s_specifier(va_arg(*args, char*), buffer);
case 'p':
return p_specifier(va_arg(*args, void*), buffer);
case CONVERSION_TRIGGER:
return print_char(CONVERSION_TRIGGER, buffer);
}
}
char* handle_format_char(char* restrict format, va_list* args, output_buffer* buffer)
{
if (*format == CONVERSION_TRIGGER)
{
handle_conversion(*(++format), args, buffer);
}
else
{
print_char(*format, buffer);
}
return format;
}
int my_printf(char* restrict format, ...)
{
output_buffer buffer = {
.index = 0,
.count = 0
};
va_list args;
va_start(args, format);
for (; *format; format++)
{
format = handle_format_char(format, &args, &buffer);
}
va_end(args);
flush(&buffer);
return buffer.count;
}
int main()
{
// Test cases (comparisons between printf and my_printf)
// %d, %o, %u and %x conversion specifiers
int d[] = {255, -255, 0, 235236, -928374};
for (int i = 0; i < 5; i++)
{
printf("Setting arg to %d\n\n", d[i]);
printf(" (return value: %d)\n", printf("Use of %%d conversion specifier with arg: %d", d[i]));
printf(" (return value: %d)\n", my_printf("Use of %%d conversion specifier with arg: %d", d[i]));
puts("");
printf(" (return value: %d)\n", printf("Use of %%o conversion specifier with arg: %o", d[i]));
printf(" (return value: %d)\n", my_printf("Use of %%o conversion specifier with arg: %o", d[i]));
puts("");
printf(" (return value: %d)\n", printf("Use of %%u conversion specifier with arg: %u", d[i]));
printf(" (return value: %d)\n", my_printf("Use of %%u conversion specifier with arg: %u", d[i]));
puts("");
printf(" (return value: %d)\n", printf("Use of %%x conversion specifier with arg: %x", d[i]));
printf(" (return value: %d)\n", my_printf("Use of %%x conversion specifier with arg: %x", d[i]));
puts("\n--------\n");
}
// %c conversion specifier
char c[] = {'a', 'Z', '!', '\0', ';'};
for (int i = 0; i < 5; i++)
{
printf("Setting arg to '%c'\n\n", c[i]);
printf(" (return value: %d)\n", printf("Use of %%c conversion specifier with arg: '%c'", c[i]));
printf(" (return value: %d)\n", my_printf("Use of %%c conversion specifier with arg: '%c'", c[i]));
puts("\n--------\n");
}
// %s conversion specifier
char* s[] = {"Roger is the best", "Why\tnot?", "Hello world!", "", NULL};
for (int i = 0; i < 5; i++)
{
printf("Setting arg to \"%s\"\n\n", s[i]);
printf(" (return value: %d)\n", printf("Use of %%s conversion specifier with arg: \"%s\"", s[i]));
printf(" (return value: %d)\n", my_printf("Use of %%s conversion specifier with arg: \"%s\"", s[i]));
puts("\n--------\n");
}
// %p conversion specifier
void* p[] = {d, c, s, *s};
for (int i = 0; i < 4; i++)
{
printf("Setting arg to %p\n\n", p[i]);
printf(" (return value: %d)\n", printf("Use of %%p conversion specifier with arg: %p", p[i]));
printf(" (return value: %d)\n", my_printf("Use of %%p conversion specifier with arg: %p", p[i]));
puts("\n--------\n");
}
// Multiple conversions
puts("Multiple conversions\n");
printf(" (return value: %d)\n", printf("%%d: %d\n%%o: %o\n%%u: %u\n%%x: %x\n%%c: %c\n%%s: %s\n%%p: %p", d[0], d[1], d[2], d[3], c[0], s[0], p[0]));
printf(" (return value: %d)\n", my_printf("%%d: %d\n%%o: %o\n%%u: %u\n%%x: %x\n%%c: %c\n%%s: %s\n%%p: %p", d[0], d[1], d[2], d[3], c[0], s[0], p[0]));
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment