Exercise 8-4 (fseek - Read/write file, high level)
Chapter_8 Exercises_8-2,3 | fsize Exercise_8-5 |
Exercise 8-4 K&R, p. 179
Exercise 8-4. The standard library function
int fseek (FILE *fp, long offset, int origin)
is identical to lseek()
except that fp
is a file pointer instead of a file descriptor and the return value is an
int status, not a position. Write
fseek(). Make sure that your
fseek() coordinates properly
with the buffering done for the other functions of the library.
fseek.c K&R, p. 175-179 download
#include <string.h> // for strcpy(), strcat(), strlen()
#include <stdlib.h> // for exit(), malloc(), free(), abs()
#include <fcntl.h> // for open(), creat(), O_RDONLY, O_WRONLY
#include <unistd.h> // for read(), write(), close(), lseek(), unlink()
enum bool {FALSE, TRUE};
#define NULL ((void *)0)
#define EOF (-1)
#define FLCH (EOF-1) // flush char
#define BUFSIZ 1024
#define OPEN_MAX 20 // max no of files open at once
#define SEEK_SET 0 // beginning of file
#define SEEK_CUR 1 // current position
#define SEEK_END 2 // end of file
typedef struct _flags
{ // bit fields
unsigned _READ : 1; // file open for reading
unsigned _WRITE : 1; // file open for writing
unsigned _UNBUF : 1; // file is unbuffered
unsigned _EOF : 1; // EOF has occurred on this file
unsigned _ERR : 1; // error occurred on this file
} FLAGS;
typedef struct _iobuf
{
int cnt; // count, characters left in the buffer
char *ptr; // pointer to next character position
char *base; // location of buffer
FLAGS flag; // mode of file access
int fd; // file descriptor
} FILE;
extern FILE _iob[OPEN_MAX]; // declaration without definition
#define stdin (&_iob[0]) // _iob
#define stdout (&_iob[1]) // _iob + 1
#define stderr (&_iob[2]) // _iob + 2
FILE _iob[OPEN_MAX] = // definition
{
{0, (char *)0, (char *)0, {1,0}, 0}, // stdin
{0, (char *)0, (char *)0, {0,1}, 1}, // stdout
{0, (char *)0, (char *)0, {0,1,1}, 2} // stderr
};
int _fillbuf(FILE *);
int _flushbuf(int, FILE *);
// FILE *p
#define feof(p) ((p)->flag._EOF != 0) // bit pos 3 of FILE.flag
#define ferror(p) ((p)->flag._ERR != 0) // bit pos 4 of FILE.flag
#define fileno(p) ((p)->fd) // index of _iob[]
#define isopen(p) ((p)->flag._READ || (p)->flag._WRITE)
#define getc(p) (--(p)->cnt >= 0 \
? (unsigned char) *(p)->ptr++ : _fillbuf(p))
#define putc(c,p) ((--(p)->cnt >= 0 && (c) != '\n') \
? *(p)->ptr++ = (c) : _flushbuf((c),p))
#define getchar() getc(stdin)
#define putchar(c) putc((c), stdout)
#define PERM 0666 // RW for owner, group, others
#define remove(fn) unlink(fn) // file name
void error(char *); // print error message and die
// set position in *fp for the next read or write:
int fseek(FILE *fp, long offset, int origin);
// read n bytes from position pos, return no of bytes read:
int get (FILE *fp, long pos, char *buf, int n);
// open file, return file pointer:
FILE * fopen(char *name, char *mode);
void filecopy(FILE *, FILE *);
int fflush(FILE *);
int fclose(FILE *);
int main() // bit fields variant
{
FILE *fp;
char buf[BUFSIZ];
if ((fp = fopen("test.txt", "w")) == NULL)
{error("fseek: can't create \"test.txt\"");}
if (write(fp->fd, "Hello!\n", 7) != 7) // write
{error("fseek: write error on file \"test.txt\"");}
fseek(fp, 0L, SEEK_SET); // offset 0 from the beginning of file
if (write(fp->fd, "Hello, ", 7) != 7) // overwrite
{error("fseek: write error on file \"test.txt\"");}
// fseek(fp, 7L, SEEK_SET); // offset 7 from the beginning of file
// fseek(fp, 0L, SEEK_CUR); // offset 0 from the current position
// fseek(fp, 0L, SEEK_END); // offset 0 from the end of file
if (write(fp->fd, "world!\n", 7) != 7) // append
{error("fseek: write error on file \"test.txt\"");}
fclose(fp);
if ((fp = fopen("test.txt", "r")) == NULL)
{error("fseek: can't open \"test.txt\"");}
if (get(fp, 0, buf, 7) == 7) // get first 7 chars (bytes)
{write(1, buf, 7);} // write to stdout (1)
if (get(fp, 7, buf, 7) == 7) // get more 7 chars (bytes)
{write(1, buf, 7);} // write to stdout (1)
fclose(fp);
remove("test.txt"); // delete file
exit(0);
}
// print an error message and die (end program)
void error(char *msg)
{ // write on stderr (2)
fflush(NULL); // flush all buffers before ending program
write(2, msg, strlen(msg)*sizeof(char));
write(2, "\n", 1);
exit(1); // end program from outside main()
}
// set position in *fp for the next read or write:
int fseek(FILE *fp, long offset, int origin)
{ // we assume all files are created or opened with fopen()
int pos; // position
if (fp == NULL || !isopen(fp))
{return EOF;} // signal error
if (fp->base != NULL)
{
if (fp->flag._UNBUF == 0 && fp->flag._READ == 1)
{ // buffered, open for reading
if (origin == SEEK_CUR)
{ // if offset within buffer, no need for lseek()
if (offset >= 0 && offset <= fp->cnt)
{ // offset after fp->ptr, before end of buffer
fp->cnt -= offset;
fp->ptr += offset;
return 0; // no error
} // fp->ptr - fp->base >= abs(offset)
else if (offset < 0 && fp->ptr + offset >= fp->base)
{ // offset before fp->ptr, after start of buffer
fp->cnt -= offset; // fp->cnt += abs(offset);
fp->ptr += offset; // fp->ptr -= abs(offset);
fp->flag._EOF = 0; // not at the end of file
return 0; // no error
}
}
// else // we need lseek()
fp->cnt = 0; // reset
fp->ptr = fp->base;
}
else if (fp->flag._WRITE == 1) // open for writing
{ // must flush even if unbuffered, see _flushbuf()
if(fflush(fp) == EOF) // not zero
{return EOF;}
}
}
pos = lseek(fp->fd, offset, origin);
// at end of file, lseek() returns a positive value, not EOF
if (pos >= 0) // no error
{ // we may be at the end of file for writing
return 0; // signal no error
}
else // error
{
fp->flag._ERR = 1;
return EOF; // signal error
}
}
// read n bytes from position pos, return no of bytes read:
int get (FILE *fp, long pos, char *buf, int n)
{
if (fseek(fp, pos, SEEK_SET) >= 0)
{return read(fp->fd, buf, n);}
// else
return -1;
}
// open file, return file pointer:
FILE * fopen(char *name, char *mode)
{
int fd;
FILE *fp;
if (*mode != 'r' && *mode != 'w' && *mode != 'a')
{return NULL;}
for (fp = _iob; fp < _iob + OPEN_MAX; fp++)
{
if (!isopen(fp)) {break;} // found free slot
}
if (fp >= _iob + OPEN_MAX) // no free slot
{return NULL;}
if(*mode == 'w')
{fd = creat(name, PERM);}
else if (*mode == 'a')
{
if ((fd = open(name, O_WRONLY, 0)) == -1)
{fd = creat(name, PERM);}
lseek(fd, 0L, SEEK_END);
}
else {fd = open(name, O_RDONLY, 0);}
if (fd == -1) // could not access name
{return NULL;}
fp->fd = fd;
fp->cnt = 0;
fp->base = NULL;
if (*mode == 'r') // we do not consider read/write operations
{fp->flag._READ = 1;} // either read
else {fp->flag._WRITE = 1;} // or write
return fp;
}
int _fillbuf(FILE *fp) // allocate and fill input buffer
{
int bufsize;
if (fp->flag._READ == 0 || fp->flag._EOF == 1 || fp->flag._ERR == 1)
{return EOF;} // we do not consider read/write operations
// else
bufsize = (fp->flag._UNBUF == 1) ? 1 : BUFSIZ;
if(fp->base == NULL) // no buffer yet
{
if ((fp->base = (char *) malloc(bufsize)) == NULL)
{return EOF;} // can't get buffer
}
fp->ptr = fp->base;
fp->cnt = read(fp->fd, fp->ptr, bufsize);
if (--fp->cnt < 0)
{
if (fp->cnt == -1)
{fp->flag._EOF = 1;}
else {fp->flag._ERR = 1;}
fp->cnt = 0; // reset
return EOF;
}
return (unsigned char) *fp->ptr++;
}
int _flushbuf(int c, FILE *fp)
{
int bufsize;
if (fp->flag._WRITE == 0 || fp->flag._EOF == 1 || fp->flag._ERR == 1)
{return EOF;} // we do not consider read/write operations
// else
bufsize = (fp->flag._UNBUF == 1) ? 1 : BUFSIZ;
if(fp->base == NULL) // no buffer yet
{
if ((fp->base = (char *) malloc(bufsize)) == NULL)
{return EOF;} // can't get buffer
}
else // flush buffer
{
if (c != FLCH) // flush char, used by fflush()
{fp->cnt++;} // account for the decrementation in putc()
if (write(fp->fd, fp->base, bufsize-fp->cnt) != bufsize-fp->cnt)
{return EOF;} // fp->ptr-fp->base == bufsize-fp->cnt
}
fp->ptr = fp->base; // reset
fp->cnt = bufsize; // reset
if (c != '\n' && c != FLCH) // flush char
{ // account for adding c
fp->cnt--;
*fp->ptr++ = c;
}
else if (c == '\n')
{write(fp->fd, "\n", 1);}
// else c == FLCH, do nothing
if (fp->cnt < 0) // if BUFSIZ <= 0
{
fp->flag._ERR = 1;
fp->cnt = 0; // reset (must reallocate buffer)
return EOF;
}
// else
if (c == FLCH) {return 0;} // do not signal error
// else
return c;
}
// copy from ifp (input) to ofp (output)
void filecopy(FILE *ifp, FILE *ofp)
{
int c;
while ((c = getc(ifp)) != EOF)
{
putc(c, ofp);
}
}
int fflush(FILE *fp)
{
int i, error = FALSE;
if (fp == NULL) // fflush(NULL) flushes all output streams
{
for (i = 0; i < OPEN_MAX; i++)
{
if (_iob[i].base != NULL && _iob[i].flag._WRITE != 0)
{ // _iob + i
if (_flushbuf(FLCH, &_iob[i]) == EOF)
{error = TRUE;}
}
}
if (error) {return EOF;}
else return 0;
}
// else
if (fp->base == NULL) {return 0;}
// else
return _flushbuf(FLCH, fp); // returns 0 if no error
}
int fclose(FILE *fp)
{
fflush(fp);
fp->cnt = 0;
fp->ptr = NULL; // fp->ptr must not be freed (it was not allocated)
free(fp->base); // free(NULL) does nothing (fp->base was allocated)
fp->base = NULL;
fp->flag._READ = fp->flag._WRITE = fp->flag._UNBUF = 0;
fp->flag._EOF = fp->flag._ERR = 0;
close(fp->fd);
fp->fd = 0; // initial value (after initialization)
}
/*
gcc fseek.c -o fseek
./fseek
Hello, world!
*/
Note: See also GitHub-kr_(8.4) and clc-wiki-kr_(8.4) solutions.
Chapter_8 Exercises_8-2,3 | BACK_TO_TOP | fsize Exercise_8-5 |
Comments
Post a Comment