get_next_line

a function that reads n bytes at a time and returns the next line of input
git clone https://github.com/TanguyAndreani/get_next_line
Log | Files | Refs | README

commit 3a96e8f2ce90ca5f41738da115208feb5cd754f6
Author: Tanguy Andreani <tanguy.andreani@tuta.io>
Date:   Tue, 18 Feb 2020 19:39:28 +0100

first commit

Diffstat:
A.gitignore | 3+++
AREADME.md | 46++++++++++++++++++++++++++++++++++++++++++++++
Aexample.c | 16++++++++++++++++
Aget_next_line.c | 7+++++++
Aget_next_line.h | 19+++++++++++++++++++
Aget_next_token.c | 26++++++++++++++++++++++++++
Aget_next_token_select.c | 13+++++++++++++
Agnl.h | 4++++
Autils.c | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 207 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +a.out +*.o+ \ No newline at end of file diff --git a/README.md b/README.md @@ -0,0 +1,45 @@ +# get_next_line + +| Resource | Link | +| - | - +home | <https://tanguyandreani.me> +code | <https://github.com/TanguyAndreani/get_next_line> +bugs | <https://github.com/TanguyAndreani/get_next_line/issues> +email | <mailto:tanguy.andreani@tuta.io> + +The goal of this exercise is to write a function `get_next_line` which will +read a constant amount of bytes at a time from a file descriptor until it +detects a newline. It should then return a copy of the whole line while keeping +the excess bytes in a safe place. A successive call will return the next line +or NULL if there is nothing left to read. + +## Example + +Let's say `GNL_READ_SIZE` is 4. + +```bash +gcc *.c +echo -n 'hello\nworld\n' | ./a.out +``` + +(The main loop is in `example.c`) + +At first read, we get `hell` into our buffer of size `GNL_READ_SIZE`. Since +there is no delimeter `\n` in there, we append the whole thing to our result +string. + +At second read, we get `o\nwo`, since there is a delimeter in here, we append +the first part to our result string. Then we shift the whole buffer to the left +until it looks like `wo`. We then return our result string. + +A second call is issued. Our result string is empty again. + +Because our buffer isn't empty and doesn't content any delimeter, we append its +content to our result string. Then we read for a third time. We get `rld\n`. +Since there is a delimeter here, we append `rld` to our result string and we +shift the buffer until after the `\n`. We return our result string. + +A third call is issued. + +Since we reached the End of input, read(2) will return 0. get_next_line() will +then return NULL. It's the end of our main loop.+ \ No newline at end of file diff --git a/example.c b/example.c @@ -0,0 +1,15 @@ +#include <stdio.h> +#include <stdlib.h> + +#include "gnl.h" + +int main(void) +{ + char *line; + while ((line = get_next_line(0))) { + printf("-> [%s]\n", line); + free(line); + } + + return (0); +}+ \ No newline at end of file diff --git a/get_next_line.c b/get_next_line.c @@ -0,0 +1,6 @@ +#include "get_next_line.h" + +char *get_next_line(int fd) +{ + return (get_next_token(fd, "\n")); +}+ \ No newline at end of file diff --git a/get_next_line.h b/get_next_line.h @@ -0,0 +1,18 @@ +#ifndef GETNEXTLINE_H_ +#define GETNEXTLINE_H_ + +#include <stdlib.h> +#include <unistd.h> +#include <stddef.h> + +#include "gnl.h" + +#define GNL_READ_SIZE 4 //4096 + +int gnl_string_contains(char c, char *str); +int gnl_match_position(char *str, char *delim, int *found); +int gnl_lshift_string(char *str, int size, int n); +char *gnl_realloc_and_append(char **dest, int *dest_size, char *src, + int src_size); + +#endif+ \ No newline at end of file diff --git a/get_next_token.c b/get_next_token.c @@ -0,0 +1,25 @@ +#include "get_next_line.h" + +char *get_next_token(int fd, char *delim) +{ + static char buf[GNL_READ_SIZE + 1]; + static int in_buf; + int ss_size = 0, toksize, has_delim; + char *ss = NULL; + + while (in_buf > 0 || (in_buf = read(fd, buf, GNL_READ_SIZE)) > 0) { + buf[in_buf] = '\0'; + + toksize = gnl_match_position(buf, delim, &has_delim); + if (!gnl_realloc_and_append(&ss, &ss_size, buf, toksize)) + return (NULL); + + if (has_delim) { + in_buf = gnl_lshift_string(buf, in_buf, toksize + 1); + return (ss); + } + in_buf = 0; + } + + return ((ss_size > 0) ? ss : NULL); +}+ \ No newline at end of file diff --git a/get_next_token_select.c b/get_next_token_select.c @@ -0,0 +1,12 @@ +#include "get_next_line.h" + +char *get_next_token_select(int fd, char *delim, int (*select)(char *tok)) +{ + char *tok = get_next_token(fd, delim); + if (!tok || (select && select(tok))) + return (tok); + else { + free(tok); + return (NULL); + } +}+ \ No newline at end of file diff --git a/gnl.h b/gnl.h @@ -0,0 +1,3 @@ +char *get_next_line(int fd); +char *get_next_token_select(int fd, char *delim, int (*select)(char *tok)); +char *get_next_token(int fd, char *delim);+ \ No newline at end of file diff --git a/utils.c b/utils.c @@ -0,0 +1,72 @@ +#include "get_next_line.h" + +int gnl_string_contains(char c, char *str) +{ + while (*str) { + if (*str == c) + return (1); + str++; + } + return (0); +} + +int gnl_match_position(char *str, char *delim, int *found) +{ + int i = 0; + for (; str[i]; i++) + if (gnl_string_contains(str[i], delim)) { + *found = 1; + return (i); + } + *found = 0; + return (i); +} + +/* + * lshift will shift a string str by n positions to the left and return the + * string's new length. + * + * e.g.: lshift("hello", 5, 2) will result in the following array: + * { 'l', 'l', 'o', '\0', 'o', '\0'} + * with return value 3 + * + * You can think of it has a function that gives you the last size - n + * characters. + */ + +int gnl_lshift_string(char *str, int size, int n) +{ + if (size < n || n <= 0) + return (size); + + for (int i = n; i < size; i++) + str[i - n] = str[i]; + str[size - n] = '\0'; + + return (size - n); +} + +/* + * A combination of realloc(3) and strncat(3) with some extra work + */ + +char *gnl_realloc_and_append(char **dest, int *dest_size, char *src, + int src_size) +{ + char *new_string = malloc(*dest_size + src_size + 1); + if (!new_string) + return (NULL); + + for (int i = 0; i < *dest_size; i++) + new_string[i] = (*dest)[i]; + + if (*dest) + free(*dest); + + for (int i = 0; i < src_size; i++) + new_string[*dest_size + i] = src[i]; + new_string[*dest_size + src_size] = '\0'; + + *dest_size += src_size; + return ((*dest = new_string)); +}+ \ No newline at end of file