2024-02-20 04:08:45 +05:30
|
|
|
/*
|
|
|
|
* freadln.c
|
|
|
|
*
|
|
|
|
* The `freadln` function reads a line from STDIN into a string, allocating
|
|
|
|
* memory for it.
|
|
|
|
*
|
2024-04-07 21:19:11 +05:30
|
|
|
* NOTE: Test: Declare macro NO_SIZE_T_FORMAT when compiling if your compiler
|
|
|
|
* or glibc do not support `%zu` format specifier.
|
2024-04-07 20:08:40 +05:30
|
|
|
* NOTE: Test: Declare macro POSIX when compiling if you're using a POSIX
|
|
|
|
* system.
|
|
|
|
*
|
2024-03-10 14:20:09 +05:30
|
|
|
* TODO: Figure out potential problems
|
2024-03-10 16:38:28 +05:30
|
|
|
* TODO: Add 'flushing' of STDIN (while there are characters, read them) before
|
|
|
|
* the input reading loop to avoid input queues
|
2024-04-07 14:10:29 +05:30
|
|
|
*
|
|
|
|
* Author: Intel A80486DX2-66
|
|
|
|
* License: Creative Commons Zero 1.0 Universal
|
2024-02-20 04:08:45 +05:30
|
|
|
*/
|
|
|
|
|
|
|
|
#include "freadln.h"
|
|
|
|
|
2024-03-10 17:09:10 +05:30
|
|
|
int freadln(FILE* f, char** output, size_t* length_out) {
|
2024-02-20 04:08:45 +05:30
|
|
|
/*
|
|
|
|
* 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`
|
2024-03-10 18:08:14 +05:30
|
|
|
* freadln_EOF: end of file
|
2024-02-20 04:08:45 +05:30
|
|
|
* freadln_ERROR: an error occurred (see errno)
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (output == NULL)
|
|
|
|
return freadln_ERROR;
|
|
|
|
|
2024-03-10 16:42:05 +05:30
|
|
|
// NOTE: if the file is STDIN, flush STDOUT before waiting for input to
|
|
|
|
// make sure a prompt is displayed
|
2024-03-10 17:20:39 +05:30
|
|
|
if (f == stdin)
|
|
|
|
fflush(stdout);
|
2024-03-10 16:42:05 +05:30
|
|
|
|
2024-03-10 17:10:02 +05:30
|
|
|
freadln_length_type length = 0; // initial length
|
2024-02-20 04:08:45 +05:30
|
|
|
|
|
|
|
*output = malloc((length + 1) * sizeof(char));
|
2024-03-10 17:36:20 +05:30
|
|
|
if (*output == NULL) {
|
|
|
|
errno = EINVAL;
|
2024-02-20 04:08:45 +05:30
|
|
|
return freadln_ERROR;
|
2024-03-10 17:36:20 +05:30
|
|
|
}
|
2024-02-20 04:08:45 +05:30
|
|
|
|
|
|
|
int character;
|
2024-04-06 00:10:19 +05:30
|
|
|
while ((character = fgetc(f)) != EOF && character != EOT
|
2024-02-20 04:08:45 +05:30
|
|
|
/* 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;
|
2024-03-10 17:55:59 +05:30
|
|
|
freadln_epilogue;
|
|
|
|
return freadln_ERROR;
|
2024-02-20 04:08:45 +05:30
|
|
|
} 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;
|
2024-03-10 17:55:59 +05:30
|
|
|
freadln_epilogue;
|
|
|
|
return freadln_ERROR;
|
2024-02-20 04:08:45 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
*output = temp;
|
|
|
|
}
|
|
|
|
|
|
|
|
errno = 0;
|
2024-03-10 17:55:59 +05:30
|
|
|
freadln_epilogue;
|
2024-03-10 18:08:14 +05:30
|
|
|
if (character == EOF)
|
|
|
|
return freadln_EOF;
|
2024-03-10 17:55:59 +05:30
|
|
|
return freadln_OK;
|
2024-02-20 04:08:45 +05:30
|
|
|
}
|
2024-03-10 16:44:38 +05:30
|
|
|
|
|
|
|
#ifdef TEST
|
2024-04-07 21:19:11 +05:30
|
|
|
# include <inttypes.h>
|
|
|
|
# include <stdint.h>
|
|
|
|
|
2024-04-07 20:08:40 +05:30
|
|
|
# if POSIX
|
|
|
|
# include <unistd.h>
|
|
|
|
# define SLEEP_FN sleep
|
|
|
|
# define DO_SLEEP 1
|
|
|
|
# elif defined(_WIN32) || defined(WIN32)
|
2024-03-10 18:10:51 +05:30
|
|
|
# include <windows.h>
|
2024-04-07 20:20:39 +05:30
|
|
|
# define SLEEP_FN(x) Sleep((DWORD) (x))
|
2024-03-10 18:10:51 +05:30
|
|
|
# define DO_SLEEP 1
|
2024-04-07 20:11:52 +05:30
|
|
|
# elif defined(__CYGWIN__) || defined(__unix__) || (defined(__APPLE__) && \
|
|
|
|
defined(__MACH__)) || defined(__linux__) || defined(__FreeBSD__) || \
|
2024-03-10 18:16:24 +05:30
|
|
|
defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || \
|
|
|
|
defined(__DragonFly__) || defined(__MINGW32__) || defined(__MINGW64__)
|
2024-03-10 18:10:51 +05:30
|
|
|
# include <unistd.h>
|
2024-04-07 20:04:24 +05:30
|
|
|
# define SLEEP_FN(x) usleep((x) * 1000ULL)
|
2024-03-10 18:10:51 +05:30
|
|
|
# define DO_SLEEP 1
|
|
|
|
# else
|
2024-03-13 10:49:40 +05:30
|
|
|
# define SLEEP_FN(...)
|
2024-03-10 18:10:51 +05:30
|
|
|
# define DO_SLEEP 0
|
|
|
|
# endif
|
|
|
|
# if DO_SLEEP
|
|
|
|
# include <time.h>
|
|
|
|
# endif
|
|
|
|
|
2024-04-07 21:19:11 +05:30
|
|
|
# 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
|
|
|
|
|
2024-04-07 21:22:01 +05:30
|
|
|
# define YN(boolean) ((boolean) ? "yes" : "no")
|
|
|
|
|
2024-03-10 16:44:38 +05:30
|
|
|
int main(void) {
|
2024-03-10 17:09:10 +05:30
|
|
|
// stdin test
|
2024-03-10 16:44:38 +05:30
|
|
|
printf("Type something> ");
|
|
|
|
char* line;
|
2024-03-13 10:52:33 +05:30
|
|
|
if (finreadln(&line, NULL) == freadln_ERROR) {
|
2024-03-10 16:44:38 +05:30
|
|
|
perror("freadln");
|
2024-04-07 21:26:49 +05:30
|
|
|
return EXIT_FAILURE;
|
2024-03-10 16:44:38 +05:30
|
|
|
}
|
|
|
|
printf("Input string: '%s'\n", line);
|
2024-03-10 17:09:10 +05:30
|
|
|
|
|
|
|
// 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;
|
2024-03-10 18:10:51 +05:30
|
|
|
|
|
|
|
printf("Waiting for 4 seconds...\n");
|
|
|
|
fflush(stdout);
|
|
|
|
|
|
|
|
clock_t start;
|
2024-03-10 17:17:41 +05:30
|
|
|
for (int i = 0; i < 4; i++) {
|
2024-03-10 18:10:51 +05:30
|
|
|
#if DO_SLEEP
|
|
|
|
start = clock();
|
|
|
|
#endif
|
|
|
|
|
2024-03-10 17:09:10 +05:30
|
|
|
size_t line_length;
|
2024-03-10 18:08:14 +05:30
|
|
|
int result = freadln(f, &line, &line_length);
|
|
|
|
if (result == freadln_ERROR) {
|
2024-03-10 17:09:10 +05:30
|
|
|
perror("freadln");
|
|
|
|
exit(EXIT_FAILURE);
|
2024-03-10 18:08:14 +05:30
|
|
|
} else if (result == freadln_EOF || feof(f)) {
|
2024-04-07 21:22:01 +05:30
|
|
|
printf("File: EOF, breaking the loop (returned by function? %s, "
|
|
|
|
"feof? %s)\n", YN(result == freadln_EOF), YN(feof(f)));
|
2024-03-10 17:17:41 +05:30
|
|
|
break;
|
2024-03-10 17:09:10 +05:30
|
|
|
}
|
2024-04-07 21:19:11 +05:30
|
|
|
printf("File, line #%d: '%s' (%" PRIuSIZE " characters)\n", i + 1, line,
|
|
|
|
(SIZE_T_FORMAT) line_length);
|
2024-03-10 18:10:51 +05:30
|
|
|
|
|
|
|
SLEEP_FN(1000 - ((long double) (clock() - start) * 1000.l) /
|
|
|
|
CLOCKS_PER_SEC);
|
2024-03-10 17:09:10 +05:30
|
|
|
}
|
|
|
|
fclose(f);
|
2024-03-10 18:10:51 +05:30
|
|
|
fflush(stdout);
|
2024-03-10 17:09:10 +05:30
|
|
|
|
2024-03-10 16:44:38 +05:30
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|