280 lines
7.6 KiB
C
280 lines
7.6 KiB
C
#include "tome_utils.h"
|
|
|
|
#define strlen invalid // make sure that the built-in strlen is not used
|
|
|
|
#ifndef BUFFER_SIZE // If the GCC compiler does not define any value
|
|
#define BUFFER_SIZE 40 // Give it a default one (lines)
|
|
#endif // This should make clangd shut up about it
|
|
|
|
#ifndef DEBUG
|
|
#define DEBUG FALSE
|
|
#endif
|
|
|
|
// INFO: THINGS TO KEEP IN MIND:
|
|
// printf is not used in this challenge, by default it's not being used unless
|
|
// it's for debugging for simplicity sake.
|
|
|
|
/*
|
|
* @info This is a simple implementation of the strlen function
|
|
*
|
|
* @brief Returns the length of a string
|
|
* @param str The string to measure
|
|
* @return The length of the string
|
|
* @error Returns 0 if str is NULL
|
|
*/
|
|
int strlen(const char *str) {
|
|
if (str == NULL) {
|
|
return 0;
|
|
}
|
|
int i = 0;
|
|
while (str[i] != '\0') {
|
|
i++;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
* @info crude and basic implementation of "print"
|
|
*
|
|
* @brief Writes a string to STDOUT
|
|
* @param str The string to write
|
|
* @error does nothing if str is NULL
|
|
*/
|
|
void printer(char *str) {
|
|
if (str == NULL) {
|
|
return;
|
|
}
|
|
write(1, str, strlen(str));
|
|
}
|
|
|
|
/*
|
|
* @info Convenience function to get the file descriptor
|
|
*
|
|
* @brief Returns the file descriptor of a file
|
|
* @param filename The name of the file
|
|
* @param mode The mode to open the file in
|
|
* @return The file descriptor of the file
|
|
* @error Returns -1 if the file cannot be opened
|
|
*/
|
|
int get_fd(char *filename, int mode) {
|
|
if (filename == NULL)
|
|
return ERR;
|
|
int fd = open(filename, mode);
|
|
if (fd == ERR)
|
|
return ERR;
|
|
return fd;
|
|
}
|
|
|
|
/*
|
|
* @info This function checks if a string contains a newline character
|
|
*
|
|
* @brief Returns TRUE (1) if the string contains a newline character, FALSE (0)
|
|
* otherwise
|
|
* @param str The string to check
|
|
* @error Returns ERR (-1) if str is NULL
|
|
*/
|
|
int newlineexists(const char *str) {
|
|
if (str == NULL)
|
|
return ERR;
|
|
for (int i = 0; str[i] != '\0'; i++) {
|
|
if (str[i] == '\n') {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* @info This function concatenates two strings
|
|
*
|
|
* @brief Returns a new string that is the concatenation of str1 and str2
|
|
* @param str1 The first string
|
|
* @param str2 The second string
|
|
* @return The concatenated string
|
|
* @error Returns NULL if str1 or str2 is NULL, or if memory allocation fails
|
|
*/
|
|
char *concat(char const *str1, char const *str2) {
|
|
char *result;
|
|
int i, j;
|
|
// Sanity check
|
|
if (!str1 || !str2)
|
|
return NULL;
|
|
result = malloc(strlen(str1) + strlen(str2) + 1); // +1 for eof
|
|
|
|
if (result == NULL)
|
|
return NULL;
|
|
for (i = 0; str1[i] != '\0'; i++) {
|
|
result[i] = str1[i];
|
|
}
|
|
if (DEBUG)
|
|
printf("[DEBUG]: Concatenating: [%s] + [%s]\n", str1, str2);
|
|
for (j = 0; str2[j] != '\0'; j++, i++) {
|
|
result[i] = str2[j];
|
|
}
|
|
result[i] = '\0';
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* @info This function reads from a file descriptor and stores the content in a
|
|
* string
|
|
*
|
|
* @brief Reads from a file descriptor and stores the content in a string
|
|
* @param fd The file descriptor to read from
|
|
* @param buf The buffer to store the content
|
|
* @param str The string to store the content
|
|
* @return The string containing the content read from the file descriptor
|
|
* @error Returns NULL if there is an error reading from the file descriptor or
|
|
* if memory allocation fails
|
|
*/
|
|
char *read_content(int fd, char *buf, char *temp, char *str) {
|
|
int bytes;
|
|
while (TRUE) {
|
|
bytes = read(fd, buf, BUFFER_SIZE);
|
|
if (bytes == ERR) {
|
|
return NULL;
|
|
}
|
|
if (DEBUG)
|
|
printf("[DEBUG]: Bytes gives : [%d]\n", bytes);
|
|
buf[bytes] = '\0';
|
|
if (DEBUG)
|
|
printf("[DEBUG]: Buffer is: [%s]\n", buf);
|
|
|
|
if (DEBUG)
|
|
printf("[DEBUG]: read %d bytes: \"%s\"\n", bytes, buf);
|
|
temp = str; // move the temporary data to str
|
|
if (temp == NULL) {
|
|
temp = malloc(sizeof(char));
|
|
if (temp == NULL) {
|
|
return NULL;
|
|
}
|
|
temp[0] = '\0';
|
|
}
|
|
if (DEBUG)
|
|
printf("[DEBUG]:We got a [%s] and [%s]\n", temp, buf);
|
|
str = concat(temp, buf); // concatenate the temporary data with the buffer
|
|
if (DEBUG)
|
|
printf("[DEBUG]:Variable temp: (%s), buf: (%s)\n", temp, buf);
|
|
if (DEBUG)
|
|
printf("[DEBUG]: Concatenated str is: [%s]\n", str);
|
|
free(temp);
|
|
|
|
if (newlineexists(str) == TRUE || bytes == 0) {
|
|
break;
|
|
}
|
|
}
|
|
free(buf);
|
|
return str;
|
|
}
|
|
|
|
/*
|
|
* @info This function returns a string up to the first newline character
|
|
*
|
|
* @brief Returns a string up to the first newline character
|
|
* @param str The string to search
|
|
* @return A string up to the first newline character
|
|
* @error Returns NULL if str is NULL or if there is no newline character in the
|
|
* string
|
|
* @details
|
|
* This function allocates memory for the new string, so it is the
|
|
* responsibility of the caller to free it when it is no longer needed.
|
|
*/
|
|
char *before_nl(const char *str) {
|
|
int i;
|
|
char *ptr = NULL;
|
|
// Sanity check
|
|
if (str == NULL || str[0] == '\0')
|
|
return NULL; // We got nothing or it's already EOF, we don't need this.
|
|
for (i = 0; str[i] != '\n' && str[i] != '\0';
|
|
i++) { // Count up until we reach a newline or EOF
|
|
continue;
|
|
}
|
|
ptr = malloc(
|
|
(i + 1 + 1) * // The str count plus the newline plus the null terminator
|
|
sizeof(char)); // Dynamically allocate *just enough* space for the str
|
|
if (ptr == NULL) {
|
|
return NULL;
|
|
}
|
|
for (i = 0; str[i] != '\n' && str[i] != '\0'; i++) { // Same thing here.
|
|
ptr[i] = str[i];
|
|
}
|
|
if (str[i] == '\n') {
|
|
ptr[i] = '\n';
|
|
i++;
|
|
}
|
|
ptr[i] = '\0';
|
|
return ptr;
|
|
}
|
|
|
|
/*
|
|
* @info This function returns what's after the first newline character
|
|
*
|
|
* @brief I'm not repeating this ^
|
|
* @param str The string to search
|
|
* @return What's after the first newline character
|
|
* @error Returns NULL if str is NULL, or malloc is going funky
|
|
*/
|
|
char *after_nl(char *str) {
|
|
int i, j;
|
|
char *ptr;
|
|
if (str == NULL)
|
|
return NULL;
|
|
for (i = 0; str[i] != '\n' && str[i] != '\0'; i++)
|
|
continue;
|
|
if (str[i] == '\n') { // Consume the \n by skipping it
|
|
i++;
|
|
}
|
|
if (str[i] == '\0') { // It's empty, might aswell drop it.
|
|
free(str);
|
|
return NULL;
|
|
}
|
|
if (DEBUG)
|
|
printf("[DEBUG]: strlen(str) = %d\n", strlen(str));
|
|
ptr = malloc((strlen(str) - i + 1) * sizeof(char));
|
|
if (ptr == NULL)
|
|
return NULL;
|
|
j = 0;
|
|
while (str[i] != '\0')
|
|
ptr[j++] = str[i++];
|
|
ptr[j] = '\0';
|
|
free(str);
|
|
return ptr;
|
|
}
|
|
|
|
/*
|
|
* @info This is similar to the get_next_line function homework I found on
|
|
* GitHub ( src: https://github.com/pvaladares/42cursus-01-get_next_line )
|
|
* Youtube ( url: https://www.youtube.com/watch?v=-Mt2FdJjVno )
|
|
* (Heavily inspired, but not copied, I can explain what I wrote ;) )
|
|
* @function tome_reader
|
|
* @brief Reads a line from a file descriptor
|
|
* @param fd The file descriptor to read from
|
|
* @return The line read from the file descriptor
|
|
*
|
|
* @details
|
|
* Here we start by defining a static variable str, which is used to store the
|
|
* content read from the file descriptor.
|
|
* We then proceed to actually get and return the line that is before the \n
|
|
* Then we just take the same string and we chop everything before the first \n
|
|
* and keep everything after.
|
|
*/
|
|
char *tome_reader(int fd) {
|
|
// INFO: technically static is a global variable, but I hope it's allowed for
|
|
// this one, removing static does the exact same thing. It's just for
|
|
// cleanness sake.
|
|
static char *str = NULL;
|
|
char *buf, *line;
|
|
char *temp = NULL;
|
|
if (fd == ERR || read(fd, 0, 0) == ERR) // Check if nothing wrong happened.
|
|
return NULL;
|
|
buf = malloc((BUFFER_SIZE + 1) * sizeof(char));
|
|
if (buf == NULL)
|
|
return NULL;
|
|
str = read_content(fd, buf, temp, str);
|
|
if (str == NULL)
|
|
return NULL;
|
|
line = before_nl(str);
|
|
str = after_nl(str);
|
|
return line;
|
|
}
|