diff --git a/phase2/tome_reader_bonus.c b/phase2/tome_reader_bonus.c new file mode 100644 index 0000000..2429c23 --- /dev/null +++ b/phase2/tome_reader_bonus.c @@ -0,0 +1,41 @@ +// INFO: INVALID SINCE SUBMISSION IS OVER + +#include "tome_utils_bonus.h" + +#ifndef OPEN_MAX // If the GCC compiler does not define any value +#define OPEN_MAX 5 // Give it a default one (lines) +#endif // This should make clangd shut up about it +int main(int argc, char *argv[]) { + char *data; + int fd[OPEN_MAX]; + // INFO: I'm sorry, I don't have much time left to implement a pipe input. + if (argc < 2) { + printer("Usage: ./tome_reader ..."); + exit(1); + } else { + for (int i = 1; i < argc; i++) { + fd[i - 1] = get_fd(argv[i], O_RDONLY); + if (fd[i] == ERR) { + printer("Error: Unable to open file\n"); + exit(1); + } + } + } + + for (int i = 0; i < argc - 1; i++) { + printer("--------------"); + // Loop until we get nothing + while ((data = tome_reader(fd[i])) != NULL) { + + printer("\n"); + printer("New line: "); + printer(data); + printer("--------------"); + free(data); + } + printer("\n"); + if (fd[i] != 0) + close(fd[i]); // Close the file descriptor + } + return 0; +} diff --git a/phase2/tome_utils_bonus.c b/phase2/tome_utils_bonus.c new file mode 100644 index 0000000..12aeb25 --- /dev/null +++ b/phase2/tome_utils_bonus.c @@ -0,0 +1,286 @@ +// INFO: INVALID SINCE SUBMISSION IS OVER + +#include "tome_utils_bonus.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 OPEN_MAX // If the GCC compiler does not define any value +#define OPEN_MAX 5 // 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(const char *str, ...) { + // screw me i guess + // va_list args; + 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[OPEN_MAX]; + 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[fd] = read_content(fd, buf, temp, str[fd]); + if (str[fd] == NULL) + return NULL; + line = before_nl(str[fd]); + str[fd] = after_nl(str[fd]); + return line; +} diff --git a/phase2/tome_utils_bonus.h b/phase2/tome_utils_bonus.h new file mode 100644 index 0000000..3396e02 --- /dev/null +++ b/phase2/tome_utils_bonus.h @@ -0,0 +1,22 @@ +// INFO: INVALID SINCE SUBMISSION IS OVER + +#include +#include +#include +#include + +// BOOLEAN VALUES +enum { + TRUE = 1, + FALSE = 0, + ERR = -1, +}; + +void printer(const char *str, ...); +int get_fd(char *filename, int mode); +int newlineexists(const char *str); +char *tome_reader(int fd); +char *before_nl(const char *str); +char *after_nl(char *str); +char *read_content(int fd, char *buf, char *temp, char *str); +char *concat(char const *string1, char const *string2);