Last active
January 28, 2021 03:42
-
-
Save ibeauregard/deb1e70299abe33b5b1a6bfdad9726dd to your computer and use it in GitHub Desktop.
printf (simple, partial implementation)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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