diff options
| -rw-r--r-- | .gitignore | 6 | ||||
| -rw-r--r-- | Makefile | 22 | ||||
| -rw-r--r-- | README.txt | 75 | ||||
| -rw-r--r-- | ft_checkpadd.c | 19 | ||||
| -rw-r--r-- | ft_printf.c | 73 | ||||
| -rw-r--r-- | ft_printf.h | 19 | ||||
| -rw-r--r-- | ft_putchar.c | 11 | ||||
| -rw-r--r-- | ft_puthexlower.c | 30 | ||||
| -rw-r--r-- | ft_puthexupper.c | 26 | ||||
| -rw-r--r-- | ft_putnbr.c | 63 | ||||
| -rw-r--r-- | ft_putstr.c | 29 | ||||
| -rw-r--r-- | ft_putunsigned.c | 29 |
12 files changed, 402 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..56a534c --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +a.out +*.a +*.o +*swp +*bak +*main.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e269455 --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +CC = cc +CFLAGS = -Wall -Werror -Wextra -g +SRCS = ft_printf.c ft_putnbr.c ft_putstr.c ft_putchar.c ft_putunsigned.c ft_puthexlower.c ft_puthexupper.c ft_checkpadd.c +OBJS = $(SRCS:.c=.o) +NAME = libftprintf.a +DEPS = ft_printf.h + +%.o : %.c $(DEPS) + $(CC) -c -o $@ $< $(CFLAGS) + +$(NAME) : $(OBJS) + ar rcs $(NAME) $(OBJS) + +all: $(NAME) + +.PHONY: clean fclean re + +clean: + rm -f *.o +fclean: clean + rm -f $(NAME) +re: fclean all diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..ce18a02 --- /dev/null +++ b/README.txt @@ -0,0 +1,75 @@ +ft_printf library +---------------- + +Description +---------- + +This is a custom implementation of printf. + +I wrote this implementation to carry on learning C and understand what printf does, to an extend. + +This is implementation is concerned with the following specifiers only: c, s, i, d, u, x, X, p. + +Instructions +----------- + +To clone the repository of this library run: + + $ git clone url directory_name + $ cd directory_name + +To compile the library run: + + $ make + +To recompile the library run: + + $ make re + +To delete all object files run: + + $ make clean + +To delete all object files and libft.a run: + + $ make fclean + +Then to use the library, ft_printf.a, with a programme, say main.c, run: + + $ cc main.c libftprintf.a + +and append each of the c files with: + + #include "path/to/ft_printf.h" + +Implementation choice +--------------------- + +ft_printf.c parses the string for the specifier. +When it finds a specifier, a helper function check the character following the specifier against conversion specifier, when the condition is true (i.e. the char following the specifier is a conversion specifier, then the argument pointer is passed to a function that will convert the argument according to the conversion specifier. + +Possible improvements: + +- I could probably merge both functions converting decimal to hexadecimal into a single function. +- At the end of my implement, I added a function, ft_checkpadd.c to check whether the address a pointer is pointing to is zero. I could try to integrate that function elsewhere. +- deal with edge case when % is the last char of a string + + +Some edge cases this implementation takes into account: + +- specifier followed by a non-conversation specifier character e.g. %t +- print(0); +- print (""); + +Relevant projects +----------------- + +- tests for printf + +Resources +--------- + +- Oceano's tutorials on variadic functions and "mini printf" +- man stdarg for variadic functions. +- man limits.h for INT_MAX, etc. +- man 3 printf diff --git a/ft_checkpadd.c b/ft_checkpadd.c new file mode 100644 index 0000000..a1881d1 --- /dev/null +++ b/ft_checkpadd.c @@ -0,0 +1,19 @@ + +#include "ft_printf.h" + +int ft_checkpadd(va_list ap) +{ + int counter; + unsigned long pointer_address; + + counter = 0; + pointer_address = ((long)(va_arg(ap, void *))); + if (pointer_address == 0) + counter += write(1, "(nil)", 5); + else + { + counter += write(1, "0x", 2); + counter += ft_puthexlower(pointer_address); + } + return (counter); +} diff --git a/ft_printf.c b/ft_printf.c new file mode 100644 index 0000000..1058f5b --- /dev/null +++ b/ft_printf.c @@ -0,0 +1,73 @@ + +// for specifier 'c', we use int because char will be promoted to +// int anyway +// +// we store address of pointer in variable cause variadic +// +// ap: argument pointer +// ap gets incremented on each call +// +// a pointer pointing to the first flag/argument +// format states how many arguments are there +// +// va_start initialise the vector argument +// +// *format is the whole string printed by printf +// +// the last condition of ft_whichspecifier is to deal with +// specifier followed by a non-conversion specifier + +#include "ft_printf.h" + +int ft_whichspecifier(va_list ap, const char *format) +{ + int counter; + + counter = 0; + if (*format == 'c') + counter += ft_putchar((char)(va_arg(ap, int))); + else if (*format == 's') + counter += ft_putstr((va_arg(ap, char *))); + else if (*format == 'i' || *format == 'd') + counter += ft_putnbr((va_arg(ap, int))); + else if (*format == 'u') + counter += ft_putunsigned((va_arg(ap, unsigned int))); + else if (*format == 'x') + counter += ft_puthexlower((va_arg(ap, unsigned int))); + else if (*format == 'X') + counter += ft_puthexupper((va_arg(ap, unsigned int))); + else if (*format == 'p') + counter += ft_checkpadd(ap); + else if (*format == '%') + counter += write(1, "%", 1); + else + { + counter += write(1, "%", 1); + //counter += ft_putchar(*format); + counter += write(1, &(*format), 1); + } + return (counter); +} + +int ft_printf(const char *format, ...) +{ + int counter; + va_list ap; + + va_start(ap, format); + counter = 0; + if (!format) + return (-1); + while (*format) + { + if (*format == '%') + { + format++; + counter += ft_whichspecifier(ap, format); + } + else + counter += ft_putchar(*format); + format++; + } + return (counter); +} diff --git a/ft_printf.h b/ft_printf.h new file mode 100644 index 0000000..5fffc0f --- /dev/null +++ b/ft_printf.h @@ -0,0 +1,19 @@ +#ifndef FT_PRINTF_H + +# define FT_PRINTF_H + +# include <limits.h> // for INT and MAX values +# include <stdarg.h> // for variadic function +# include <stdio.h> +# include <unistd.h> // for write function + +int ft_putchar(char c); +int ft_putstr(char *str); +int ft_putnbr(int n); +int ft_putunsigned(unsigned int nb); +int ft_puthexlower(unsigned long n); +int ft_puthexupper(unsigned int n); +int ft_checkpadd(va_list ap); +int ft_printf(const char *format, ...); + +#endif diff --git a/ft_putchar.c b/ft_putchar.c new file mode 100644 index 0000000..4ff8dda --- /dev/null +++ b/ft_putchar.c @@ -0,0 +1,11 @@ + +#include "ft_printf.h" + +int ft_putchar(char c) +{ + int counter; + + counter = 0; + counter = write(1, &c, 1); + return (counter); +} diff --git a/ft_puthexlower.c b/ft_puthexlower.c new file mode 100644 index 0000000..282328a --- /dev/null +++ b/ft_puthexlower.c @@ -0,0 +1,30 @@ + +// parameter is unsigned long so function does not +// overflow when LONG_MAX is the address +// we use recursion to print backwards + +#include "ft_printf.h" + +int ft_puthexlower(unsigned long n) +{ + const char hex[16] = "0123456789abcdef"; + int save; + int remainder; + int counter; + + remainder = 0; + save = 0; + counter = 0; + if (n > 15) + { + save = n; + n /= 16; + remainder = save - (n * 16); + counter += ft_puthexlower(n); + counter += write(1, &hex[remainder], 1); + } + else + counter += write(1, &hex[n], 1); + return (counter); +} + diff --git a/ft_puthexupper.c b/ft_puthexupper.c new file mode 100644 index 0000000..b5518bf --- /dev/null +++ b/ft_puthexupper.c @@ -0,0 +1,26 @@ + +// using unsigned int as a paramter to deal with with negative numbers +#include "ft_printf.h" + +int ft_puthexupper(unsigned int n) +{ + const char hex[16] = "0123456789ABCDEF"; + int save; + int remainder; + int counter; + + remainder = 0; + save = 0; + counter = 0; + if (n > 15) + { + save = n; + n /= 16; + remainder = save - (n * 16); + counter += ft_puthexupper(n); + counter += write(1, &hex[remainder], 1); + } + else + counter += write(1, &hex[n], 1); + return (counter); +} diff --git a/ft_putnbr.c b/ft_putnbr.c new file mode 100644 index 0000000..fe138ff --- /dev/null +++ b/ft_putnbr.c @@ -0,0 +1,63 @@ + +#include "ft_printf.h" + +int ft_putnbr(int n) +{ + int counter; + + counter = 0; + if (n == -2147483648) + counter += ft_putstr("-2147483648"); + else if (n < 0) + { + counter += ft_putchar('-'); + n = -n; + counter += ft_putnbr(n); + } + else if (n == 0) + counter += ft_putchar('0'); + else if (n > 9) + { + counter += ft_putnbr(n / 10); + counter += ft_putchar((n % 10) + 48); + } + else + counter += ft_putchar(n + 48); + return (counter); +} + +/* +// non-recursive implementation +int ft_putnbr(int nb) +{ + char c[11]; + int i; + long int nbl; + int counter; + + nbl = nb; + counter = 0; + if (nbl < 0) + { + nbl *= -1; + counter += write(1, "-", 1); + } + if (nbl == 0) + counter += write(1, "0", 1); + i = 0; + while (nbl) + { + c[i] = (nbl % 10) + 48; + nbl = nbl / 10; + i++; + } + while (i > 0) + { + i--; + counter += write(1, &c[i], 1); + } + c[i] = '\0'; + return (counter); +} +*/ + diff --git a/ft_putstr.c b/ft_putstr.c new file mode 100644 index 0000000..19a2c37 --- /dev/null +++ b/ft_putstr.c @@ -0,0 +1,29 @@ + +// we use putchar to count each character + +#include "ft_printf.h" + +int ft_putstr(char *str) +{ + int counter; + const char *snull = "(null)"; + + counter = 0; + if (str == NULL) + { + while (*snull) + { + counter += ft_putchar(*snull); + snull++; + } + } + else + { + while (*str) + { + counter += ft_putchar(*str); + str++; + } + } + return (counter); +} diff --git a/ft_putunsigned.c b/ft_putunsigned.c new file mode 100644 index 0000000..8908de0 --- /dev/null +++ b/ft_putunsigned.c @@ -0,0 +1,29 @@ + +#include "ft_printf.h" + +int ft_putunsigned(unsigned int nb) +{ + char c[11]; + int i; + long int nbl; + int counter; + + nbl = nb; + counter = 0; + if (nbl == 0) + counter += write(1, "0", 1); + i = 0; + while (nbl) + { + c[i] = (nbl % 10) + 48; + nbl = nbl / 10; + i++; + } + while (i > 0) + { + i--; + counter += write(1, &c[i], 1); + } + c[i] = '\0'; + return (counter); +} |
