/* * freadln.c * * The `freadln` function reads a line from STDIN into a string, allocating * memory for it. * * NOTE: Test: Declare macro NO_SIZE_T_FORMAT when compiling if your compiler * or glibc do not support `%zu` format specifier. * NOTE: Test: Declare macro POSIX when compiling if you're using a POSIX * system. * * 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: Unlicense */ #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 # include # include # include # include # if POSIX # include # define SLEEP_FN sleep # define DO_SLEEP 1 # elif defined(_WIN32) || defined(WIN32) # include # define SLEEP_FN(x) Sleep((DWORD) (x)) # 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(x) usleep((x) * 1000ULL) # define DO_SLEEP 1 # else # define SLEEP_FN(...) # define DO_SLEEP 0 # endif # if DO_SLEEP # include # endif # ifndef NO_SIZE_T_FORMAT # if defined(__TINYC__) || (defined(__STDC_VERSION__) && \ __STDC_VERSION__ < 199901L) # define NO_SIZE_T_FORMAT # endif # endif # ifdef NO_SIZE_T_FORMAT # define PRIuSIZE PRIuMAX typedef uintmax_t SIZE_T_FORMAT; # else # define PRIuSIZE "zu" typedef size_t SIZE_T_FORMAT; # endif # define YN(boolean) ((boolean) ? "yes" : "no") bool file_exists(const char* path); bool file_exists(const char* path) { FILE* f = fopen(path, "rb"); return !((f == NULL /* close file if it was successfully opened */ || (fclose(f), false)) && errno == ENOENT); } int main(void) { // stdin test printf("Type something> "); char* line; if (finreadln(&line, NULL) == freadln_ERROR) { perror("freadln"); return EXIT_FAILURE; } printf("Input string: '%s' (%" PRIuSIZE " characters)\n", line, (SIZE_T_FORMAT) strlen(line)); // file test #define TEST_FILE "freadln_test.txt" if (file_exists(TEST_FILE)) { fflush(stdout); fprintf(stderr, "Error: A file `" TEST_FILE "` exists. Are you sure " "you want to continue and\noverwrite it? [[y = Enter]/n]: "); fflush(stderr); size_t line_length; if (finreadln(&line, &line_length) == freadln_ERROR) { perror("freadln"); return EXIT_FAILURE; } else if (line_length >= 1) { if (tolower(line[0]) == 'n') return EXIT_FAILURE; } } FILE* f = fopen(TEST_FILE, "w"); if (f == NULL) { perror("fopen"); return EXIT_FAILURE; } fprintf(f, "Hello, world!\nAnother line\n\n"); FILE* new_f = freopen(TEST_FILE, "r", f); if (new_f == NULL) { perror("freopen"); return 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"); return EXIT_FAILURE; } else if (result == freadln_EOF || feof(f)) { printf("File: EOF, breaking the loop (returned by function? %s, " "feof? %s)\n", YN(result == freadln_EOF), YN(feof(f))); break; } printf("File, line #%d: '%s' (%" PRIuSIZE " characters)\n", i + 1, line, (SIZE_T_FORMAT) line_length); SLEEP_FN(1000 - ((long double) (clock() - start) * 1000.l) / CLOCKS_PER_SEC); } fclose(f); remove(TEST_FILE); fflush(stdout); return 0; } #endif