// 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; }