Invalid Argument Reported By getdelim

848 views Asked by At

I'm trying to use the getdelim function to read an entire text file's contents into a string.

Here is the code I am using:

ssize_t bytesRead = getdelim(&buffer, 0, '\0', fp);

This is failing however, with strerror(errno) saying "Error: Invalid Argument"

I've looked at all the documentation I could and just can't get it working, I've tried getline which does work but I'd like to get this function working preferably.

buffer is NULL initialised as well so it doesn't seem to be that
fp is also not reporting any errors and the file opens perfectly

EDIT: My implementation is based on an answer from this stackoverflow question Easiest way to get file's contents in C

1

There are 1 answers

1
Nominal Animal On BEST ANSWER

Kervate, please enable compiler warnings (-Wall for gcc), and heed them. They are helpful; why not accept all the help you can get?

As pointed out by WhozCraig and n.m. in comments to your original question, the getdelim() man page shows the correct usage.

If you wanted to read records delimited by the NUL character, you could use

FILE   *input; /* Or, say, stdin */
char   *buffer = NULL;
size_t  size = 0;
ssize_t length;

while (1) {
    length = getdelim(&buffer, &size, '\0', input);
    if (length == (ssize_t)-1)
        break;

    /* buffer has length chars, including the trailing '\0' */
}

free(buffer);
buffer = NULL;
size = 0;

if (ferror(input) || !feof(input)) {
    /* Error reading input, or some other reason
     * that caused an early break out of the loop. */
}

If you want to read the contents of a file into a single character array, then getdelim() is the wrong function.

Instead, use realloc() to dynamically allocate and grow the buffer, appending to it using fread(). To get you started -- this is not complete! -- consider the following code:

FILE   *input; /* Handle to the file to read, assumed already open */
char   *buffer = NULL;
size_t  size = 0;
size_t  used = 0;
size_t  more;

while (1) {

    /* Grow buffer when less than 500 bytes of space. */
    if (used + 500 >= size) {
        size_t new_size = used + 30000; /* Allocate 30000 bytes more. */
        char  *new_buffer;

        new_buffer = realloc(buffer, new_size);
        if (!new_buffer) {
            free(buffer); /* Old buffer still exists; release it. */
            buffer = NULL;
            size = 0;
            used = 0;
            fprintf(stderr, "Not enough memory to read file.\n");
            exit(EXIT_FAILURE);
        }

        buffer = new_buffer;
        size = new_size;
    }

    /* Try reading more data, as much as fits in buffer. */
    more = fread(buffer + used, 1, size - used, input);
    if (more == 0)
        break; /* Could be end of file, could be error */

    used += more;
}

Note that the buffer in this latter snippet is not a string. There is no terminating NUL character, so it's just an array of chars. In fact, if the file contains binary data, the array may contain lots of NULs (\0, zero bytes). Assuming there was no error and all of the file was read (you need to check for that, see the former example), buffer contains used chars read from the file, with enough space allocated for size. If used > 0, then size > used. If used == 0, then size may or may not be zero.

If you want to turn buffer into a string, you need to decide what to do with the possibly embedded \0 bytes -- I recommend either convert to e.g. spaces or tabs, or move the data to skip them altogether --, and add the string-terminating \0 at end to make it a valid string.