Search code examples
cfile-iostructarmfat

C Programming with ARM - Output and input of struct to a file


I am having a bit of a problem with a struct within a program I am writing. Background; I am developing code in C on an ARMv7 chip. The system has an SD card with FAT16 filesystem for file access. I am using a library for FAT16 file read and write operations to do any work with file output/input. Currently I am declaring a struct globally before the main function;

struct config_information
{
    char *name;

    //version
    char *fwversion;

    //IP address
    char *ip;
};

//Declare structs for config information
struct config_information config;
struct config_information loadedconfig;

The pointers are for string use. I read the data into these variables using &config.name for example. I have to use a function I have written to read in characters received over the serial port into another char pointer to strcat to another, but this works correctly and I have verified the details input by a user match the recorded input (there is a quick prompt screen on a terminal window to prompt for details if anyone was wondering).

I then implement the provided library function to save the struct to a file;

fat16_write_file(CONF_FILE,&config, READBUFSIZE);

Where CONF_FILE is just the file handle for the file to use, &config is an output buffer and READBUFSIZE is the size of the buffer (defined as 148, big enough for my purposes but later I hope to change it to calculate the size of the struct to output, and then calculate the file size to be able to read it back in correctly). I have added the function definition provided by the library with the opening statement at the end for reference in case I haven't been clear.

This works perfectly, the output in the file is; Name1.1 192.168.1.1 with alot of whitespace. Obviously I input Name as the Name, 1.1 as the firmware and the IP is self explanatory.

Naively I thought I could do the reverse to read it back into the loadconfig struct I declared with this;

fat16_read_file(CONF_FILE,&loadedconfig,READBUFSIZE);

Clearly this has no hope of working as the struct doesn't have any pre-defined sizes for it to know how much to read back in.

So, how can I read the data I have saved back into the same struct that it came from? The name could be of any size, not massive but it cannot be safely predicted. The FW version will only ever be #.# and the IP is obviously within already defined limits.

Thanks for any hints or nudges in the right direction. Please ask if you feel you need more information to understand the scope.

For reference;

/**
 * \ingroup fat16_file
 * Reads data from a file.
 *
 * The data requested is read from the current file location.
 *
 * \param[in] fd The file handle of the file from which to read.
 * \param[out] buffer The buffer into which to write.
 * \param[in] buffer_len The amount of data to read.
 * \returns The number of bytes read, 0 on end of file, or -1 on failure.
 * \see fat16_write_file
 */
int16_t fat16_read_file(struct fat16_file_struct* fd, uint8_t* buffer, uint16_t buffer_len)

/**
 * \ingroup fat16_file
 * Writes data to a file.
 *
 * The data is written to the current file location.
 *
 * \param[in] fd The file handle of the file to which to write.
 * \param[in] buffer The buffer from which to read the data to be written.
 * \param[in] buffer_len The amount of data to write.
 * \returns The number of bytes written, 0 on disk full, or -1 on failure.
 * \see fat16_read_file
 */
int16_t fat16_write_file(struct fat16_file_struct* fd, const uint8_t* buffer, uint16_t buffer_len)

Solution

  • If you wish to write the struct out to a file then read it back in, you should not use pointers for its members. You should declare it like so:

    /* specify sizes according to your needs */
    #define MAX_NAME_SIZE 16
    #define MAX_FWVERSION_SIZE 16
    #define MAX_IP_SIZE 16
    
    struct config_information
    {
        char name[MAX_NAME_SIZE];
    
        //version
        char fwversion[MAX_FWVERSION_SIZE];
    
        //IP address
        char ip[MAX_IP_SIZE];
    };
    
    // write it to a file
    fat16_write_file(CONF_FILE,&config, sizeof(config_information));
    
    // read it from a file
    fat16_read_file(CONF_FILE,&loadedconfig, sizeof(config_information));