/* * freadln.c * * The `freadln` function reads a line from STDIN into a string, allocating * memory for it. * * TODO: Figure out potential problems * TODO: Add 'flushing' of STDIN (while there are characters, read them) before * the input reading loop to avoid input queues * * Author: Intel A80486DX2-66 * License: Creative Commons Zero 1.0 Universal */ #include "freadln.h" int freadln(FILE* f, char** output, size_t* length_out) { /* * The length of STDIN line is counted without any terminating characters. * * return value: * freadln_OK: no errors, the length of STDIN line has been stored in * `length_out` * freadln_EOF: end of file * freadln_ERROR: an error occurred (see errno) */ if (output == NULL) return freadln_ERROR; // NOTE: if the file is STDIN, flush STDOUT before waiting for input to // make sure a prompt is displayed if (f == stdin) fflush(stdout); freadln_length_type length = 0; // initial length *output = malloc((length + 1) * sizeof(char)); if (*output == NULL) { errno = EINVAL; return freadln_ERROR; } int character; while ((character = fgetc(f)) != EOF && character != EOT /* stop on a newline character: */ && character != '\n') { (*output)[length] = (char) character; // integer overflow and integer limit check, to keep array boundaries if ((freadln_length_type) (length + 2) <= (freadln_length_type) length) { errno = ERANGE; freadln_epilogue; return freadln_ERROR; } else length++; char* temp = realloc(*output, (length + 1) * sizeof(char)); // If the function fails to allocate new memory, return the string that // has already been accumulated. if (temp == NULL) { // keep errno; freadln_epilogue; return freadln_ERROR; } *output = temp; } errno = 0; freadln_epilogue; if (character == EOF) return freadln_EOF; return freadln_OK; } #ifdef TEST # if defined(_WIN32) || defined(WIN32) # include # define SLEEP_FN Sleep # define DO_SLEEP 1 # elif defined(__CYGWIN__) || defined(__unix__) || defined(__APPLE__) && \ defined(__MACH__) || defined(__linux__) || defined(__FreeBSD__) || \ defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || \ defined(__DragonFly__) || defined(__MINGW32__) || defined(__MINGW64__) # include # define SLEEP_FN usleep # define DO_SLEEP 1 # else # define SLEEP_FN(...) # define DO_SLEEP 0 # endif # if DO_SLEEP # include # endif int main(void) { // stdin test printf("Type something> "); char* line; if (finreadln(&line, NULL) == freadln_ERROR) { perror("freadln"); exit(EXIT_FAILURE); } printf("Input string: '%s'\n", line); // file test #define TEST_FILE "freadln_test.txt" FILE* f = fopen(TEST_FILE, "w"); if (f == NULL) { perror("fopen"); exit(EXIT_FAILURE); } fprintf(f, "Hello, world!\nAnother line\n\n"); FILE* new_f = freopen(TEST_FILE, "r", f); if (new_f == NULL) { perror("freopen"); exit(EXIT_FAILURE); } f = new_f; printf("Waiting for 4 seconds...\n"); fflush(stdout); clock_t start; for (int i = 0; i < 4; i++) { #if DO_SLEEP start = clock(); #endif size_t line_length; int result = freadln(f, &line, &line_length); if (result == freadln_ERROR) { perror("freadln"); exit(EXIT_FAILURE); } else if (result == freadln_EOF || feof(f)) { printf("File: EOF, breaking the loop (returned by function? %d, " "feof? %d)\n", result == freadln_EOF, !!feof(f)); break; } printf("File, line #%d: '%s' (%zu characters)\n", i + 1, line, line_length); SLEEP_FN(1000 - ((long double) (clock() - start) * 1000.l) / CLOCKS_PER_SEC); } fclose(f); fflush(stdout); return 0; } #endif