Spiros Gkogkas
Spiros Gkogkas

Reputation: 512

Infinite Loop While Modifying Binary File

I am trying to open a binary file read a part from it and then modify it but this ends up in an infinite loop.

Things I had in mind before writing the program:

  1. EOF is a value that is returned by a function that reads/writes from/to the stream when End of file is found or if an error occurs.
  2. File indicator will go the size of the bytes it read plus to right with every successful read/write. (i.e. if position indicator is at 0 position it will go to position 44 if it reads/writes 44 bytes after a successful read/write.)

The code below is one of my many approaches with no success:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct stoixeia
{
    char e[30];
    char t[5];
    float mo;
    int ilikia;
};


int main(void)
{
    FILE *fptr;
    struct stoixeia st;
    int c;

    if((fptr = fopen("sxoleio","rb+")) == NULL)
        exit(1);

    while((c = fread(&st,sizeof(struct stoixeia),1,fptr)) != EOF)
    {
        fseek(fptr,-44,SEEK_CUR);
        for(int i = 0; i < strlen(st.e); i++)
            st.e[i] += 1;
        fwrite(&st,sizeof(struct stoixeia),1,fptr);
    }
    fclose(fptr);
    return 0;

Other unsuccessful approaches:(feof,check for eof inside the loop etc.)

The only success I had so far was with the code below:

    while((c = fgetc(fptr)) != EOF)
    {
        fseek(fptr,-1,SEEK_CUR);
        fread(&st,sizeof(struct stoixeia),1,fptr);
        fseek(fptr,-44,SEEK_CUR);
        for(int i = 0; i < strlen(st.e); i++)
            st.e[i] += 1;
        fwrite(&st,sizeof(struct stoixeia),1,fptr);
        fseek(fptr,1,SEEK_CUR);
        fseek(fptr,-1,SEEK_CUR);
    }
    fclose(fptr);

Using fgetc() as the while condition and moving indicator by one byte to the right after the operations and then moving it back by one byte seems to trigger EOF and the program ends with success.

What is the reason for this behavior?

I really want to understand the behavior for this so anything would be much appreciated.

Thanks in advance.

Upvotes: 0

Views: 261

Answers (1)

William Pursell
William Pursell

Reputation: 212634

There are a few issues with your code, but I think the biggest misconception is the return value of fread. Its return value should not be compared to EOF. When you reach end of file, fread will return a short count. In this case, that means it will return zero. Try:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct stoixeia {
    char e[30];
    char t[5];
    float mo;
    int ilikia;
};

static void die(const char *msg) { perror(msg); exit(EXIT_FAILURE); }
int
main(int argc, char **argv)
{
        FILE *fptr;
        struct stoixeia st;
        size_t c;
        const char *path = argc > 1 ? argv[1] : "sxoleio";

        if( (fptr = fopen(path, "rb+")) == NULL ){
                die(path);
        }
        while( (c = fread(&st, sizeof st, 1, fptr)) == 1 ){
                if( fseek(fptr, -sizeof st, SEEK_CUR) == -1 ){
                        die("fseek");
                }
                for( size_t i = 0; st.e[i] && i < sizeof st.e; i++ ){
                        st.e[i] += 1;
                }
                if( fwrite(&st, sizeof st, 1, fptr) != 1 ){
                        die("fwrite");
                };
        }
        if( fclose(fptr) ){
                die("fclose");
        }
        return EXIT_SUCCESS;
}

But, it's probably cleaner to skip the seeks completely and use two different file handles. Something like:

if( (rf = fopen(path, "r")) == NULL) {
        die(path);
}
if( (wf = fopen(path, "r+")) == NULL) {
        die(path);
}
while( (c = fread(&st, sizeof st, 1, rf)) == 1) {
        for( size_t i = 0; st.e[i] && i < sizeof st.e; i++ ){
                st.e[i] += 1;
        }
        if( fwrite(&st, sizeof st, 1, wf) != 1 ){
                die("fwrite");
        };
}

Upvotes: 1

Related Questions