Papbad
Papbad

Reputation: 163

How to convert from fopen to open function?

I can't seem to work out how to convert from an fopen to open. I don't have much c experience, so this is quite overwhelming for me.

Here is what it is on its own:

In the cache_reader.c file (just the open and close functions):

void cr_close(cr_file* f){
free(f->buffer);
fclose(f->file);
}

cr_file* cr_open(char * filename, int buffersize)
{
    FILE* f;
    if ((f = fopen(filename, "r")) == NULL){
         fprintf(stderr, "Cannot open %s\n", filename);
         return 0; }

    cr_file* a=(cr_file*)malloc(sizeof(cr_file));
    a->file=f;
    a->bufferlength=buffersize;
    a->usedbuffer=buffersize;
    a->buffer=(char*)malloc(sizeof(char)*buffersize);
    refill(a);
    return a;
 }

In the cache_reader.h file:

typedef struct{
   FILE* file;        //File being read
   int bufferlength;  //Fixed buffer length
   int usedbuffer;    //Current point in the buffer
   char* buffer;      //A pointer to a piece of memory
                 //  same length as "bufferlength"
 } cr_file;
 //Open a file with a given size of buffer to cache with
 cr_file* cr_open(char* filename, int buffersize);
 //Close an open file
 void cr_close(cr_file* f);
 int refill(cr_file* buff);

In the cache_example.c file:

int main(){
char c;

 //Open a file
 cr_file* f = cr_open("text",20);  

 //While there are useful bytes coming from it
 while((c=cr_read_byte(f))!=EOF)
 //Print them
 printf("%c",c);

 //Then close the file
 cr_close(f);

//And finish
return 0;
}

I know that I need to change fclose to close, fopen to open. But I do not understand most of the other stuff. I am getting a ton of errors for everything, and I am not sure how this works out with pointers, as I barely have any experience with them. I tried to use int fileno(FILE *stream), by saying int fd = fileno(FILE *f) and then fd = fopen(filename, "r")) == NULL). This didn't work, and all examples of the open function I could find just use the name of the file, rather than a pointer of chars to specify filename... I thought the cr_close function can be done by just changing fclose to close, but this doesn't work either. I am not sure if I need to edit cache_example.c file as well.

Could anyone provide some help to put me on the right path to do this..?

Upvotes: 0

Views: 1697

Answers (3)

user8128167
user8128167

Reputation: 7676

Please note that I had two options, I could convert the fopen to open found in fcntl.h as described above, but that would have required me to change a lot of function signatures in the existing code base from FILE * to int. Instead, I decided to go with fflush and fsync which turned out to be a simpler solution while still ensuring that the file contents were written to disk immediately. The reason that fflush and fsync were easier is that they still allowed me to work with the existing FILE pointer. Then to ensure my file is written to disk immediately, I use the following. This basically gives you a potential alternative solution:

FILE *fp = fopen(myfilename, "w+");

if (fp) {
    // write contents of buffer to OS
    if (0 != fflush(fp)) {
        // error handling here
    }

    // Tell OS to write contents of buffers to storage media
    if (0 != fsync(fileno(fp))) {
        // more error handling
    } else {
        // steps to do on success
    }
} else {
    // debug log could not complete task
}

This is what chqrlie explains in this post: Difference between fflush and fsync

Upvotes: 0

Jonathan Leffler
Jonathan Leffler

Reputation: 753455

From comments

The object of the exercise is to leave the example code unchanged, but reimplement the other code to use file descriptors instead of file streams. Sadly, the header exposes the internals of the structure, unnecessarily, so the example needs to be recompiled. You will change the FILE * member to an int. You will not use any function that takes a file stream argument.

The header (cache_reader.h) should contain this (and not the structure definition):

typedef struct cr_file cr_file;

The source (cache_reader.c) should contain:

struct cr_file
{
    int file;
    int bufferlength;
    int usedbuffer;
    char *buffer;
};

This gives you an opaque type in the client (example) code, and allows you to change the structure without needing to recompile the client code (though you would have to recompile the implementation, of course — we can't work complete miracles).

Of course you can make your clients recompile their code whenever you make changes to the internals of the library. However, it is often more convenient in the long run if you can make changes and improvements to your library code without requiring the consumers (other programmers) to recompile their code. Binary compatibility is very important for big libraries, like the standard C library on a given platform. For a tiny project like this, it doesn't matter — but you'll need to learn about the larger scale issue too, at least in due course, if you stick with programming as a career.

Reworked code

Note that I concluded I needed some different members to support your use case — I needed to know the size of the allocated buffer, the amount of data actually in the buffer, and the current position for reading. I renamed the members to bufmax (your bufferlength), bufpos (your usedbuffer), and added buflen.

I wrote sample code for cr_read_byte(), and it works reading a file.

However, there is considerable work to be done to support writing too, and moving around in the file other than one byte at a time, and so on.

cache_reader.h

#ifndef CACHE_READER_H_INCLUDED
#define CACHE_READER_H_INCLUDED

typedef struct cr_file cr_file;

extern cr_file *cr_open(char *filename, int buffersize);
extern void cr_close(cr_file *f);
extern int cr_read_byte(cr_file *f);

#endif /* CACHE_READER_H_INCLUDED */

cache_reader.c

#include "cache_reader.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

struct cr_file
{
    int   file;     // File being read
    int   bufmax;   // Fixed buffer length
    int   bufpos;   // Current point in the buffer
    int   buflen;   // Amount of data in the buffer
    char *buffer;   // A pointer to a piece of memory
};

static void cr_refill(cr_file *f)
{
    if (f->bufpos >= f->buflen)
    {
        int nbytes = read(f->file, f->buffer, f->bufmax);
        if (nbytes > 0)
        {
            f->buflen = nbytes;
            f->bufpos = 0;
        }
    }
}

void cr_close(cr_file *f)
{
    free(f->buffer);
    close(f->file);
    free(f);
}

cr_file *cr_open(char *filename, int buffersize)
{
    int fd;
    if ((fd = open(filename, O_RDWR)) < 0)
    {
        fprintf(stderr, "cannot open %s for reading and writing (%d: %s)\n",
                filename, errno, strerror(errno));
        return 0;
    }

    cr_file *a = (cr_file *)malloc(sizeof(cr_file));
    char *b = (char *)malloc(sizeof(char) * buffersize);
    if (a == 0 || b == 0)
    {
        free(a);
        free(b);
        close(fd);
        fprintf(stderr, "cannot allocate %zu bytes of memory (%d: %s)\n",
                sizeof(cr_file) + buffersize, errno, strerror(errno));
        return 0;
    }
    a->file = fd;
    a->bufmax = buffersize;
    a->bufpos = 0;
    a->buflen = 0;
    a->buffer = b;
    return a;
}

int cr_read_byte(cr_file *f)
{
    if (f->bufpos >= f->buflen)
        cr_refill(f);
    if (f->bufpos >= f->buflen)
        return EOF;
    return f->buffer[f->bufpos++];
}

cache_example.c

#include "cache_reader.h"
#include <stdio.h>

int main(void)
{
    cr_file *f = cr_open("text", 20);
    if (f != 0)
    {
        int c;
        while ((c = cr_read_byte(f)) != EOF)
            putchar(c);
        cr_close(f);
    }
    return 0;
}

makefile

CFLAGS  = -std=c11 -O3 -g -Wall -Wextra
LDFLAGS =
LDLIBS  =

FILES.c = cache_example.c cache_reader.c
FILES.o = ${FILES.c:.c=.o}
FILES.h = cache_reader.h

PROG1 = cache_example

PROGRAMS = ${PROG1}

all: ${PROGRAMS}

${PROG1}: ${FILES.o}
    ${CC} -o $@ ${CFLAGS} ${FILES.o} ${LDFLAGS} ${LDLIBS}

${FILES.o}: ${FILES.h}

You can find this code (except for the skeletal makefile shown in the answer) on GitHub in my SOQ (Stack Overflow Questions) repository as files in the src/so-4901-1302 sub-directory.

Upvotes: 1

user3629249
user3629249

Reputation: 16540

the following proposed code:

  1. cleanly compiles
  2. eliminates the unneeded/unused code
  3. includes the #include statements and why they are needed
  4. performs the desired functionality
  5. uses (per the OPs request) file descriptors rather than file pointers

and now, the proposed code

// following struct not actually used in this code
#if 0
typedef struct
{
   int fd;               // file descriptor number of input file
   /*
    * int bufferlength;  // Fixed buffer length
    * int usedbuffer;    // Current point in the buffer
    * char* buffer;      // A pointer to a piece of memory
    *                    // same length as "bufferlength"
    * */
} cr_file;
#endif
-------------------------

// following 3 header files for 'open()' and 'O_RDONLY'
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

// following for 'read()' and 'close()'
#include <unistd.h>

// following for 'exit()' and 'EXIT_FAILURE'
#include <stdlib.h>

// following for 'printf()', 'perror()'
#include <stdio.h>

int main( void )
{
    //Open a file
    int fd = open("text", O_RDONLY);
    if( 0 > fd )
    { // then open failed
        perror( "open for input file failed" );
        exit( EXIT_FAILURE );
    }

    // implied else, open successful

    //While there are useful bytes coming from it
    char buf;

    while( read( fd, &buf, 1 ) > 0 ) 
    {
        printf( "%c", buf );
    }

    //Then close the file
    close( fd );

    //And finish
    return 0;
}

Upvotes: 0

Related Questions