1
0
mirror of https://gitlab.com/80486DX2-66/gists synced 2024-11-12 22:45:54 +05:30
gists/c-programming/io/freadln.c

213 lines
5.2 KiB
C

/*
* 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 <ctype.h>
# include <inttypes.h>
# include <stdbool.h>
# include <stdint.h>
# if POSIX
# include <unistd.h>
# define SLEEP_FN sleep
# define DO_SLEEP 1
# elif defined(_WIN32) || defined(WIN32)
# include <windows.h>
# 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 <unistd.h>
# define SLEEP_FN(x) usleep((x) * 1000ULL)
# define DO_SLEEP 1
# else
# define SLEEP_FN(...)
# define DO_SLEEP 0
# endif
# if DO_SLEEP
# include <time.h>
# endif
# ifndef NO_SIZE_T_FORMAT
# if defined(__TINYC__) || (defined(__STDC_VERSION__) && \
__STDC_VERSION__ < 199901L)
# define NO_SIZE_T_FORMAT
# endif
# endif
# if defined(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