Reputation: 163
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
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
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.
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
Reputation: 16540
the following proposed code:
#include
statements and why they are neededand 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