#include <stdio.h> // for BUFSIZ,, fprintf(), vfprintf(), stderr,
// SEEK_SET (0), SEEK_CUR (1), SEEK_END (2)
#include <stdlib.h> // for exit()
#include <fcntl.h> // for open(), creat()
#include <unistd.h> // for read(), write(), lseek(), unlink(), stdout (1)
#define PERM 0666 // RW for owner, group, others
void error(char *, ...);
// read n bytes from position pos, return no of bytes read
int get (int fd, long pos, char *buf, int n);
int main(int argc, char *argv[]) // read/write a file at random position
{
char buf[BUFSIZ]; // 8192 bytes
int fd, n;
if ((fd = creat("test.txt", PERM)) == -1)
{error("%s: can't create \"test.txt\", mode %03o", argv[0], PERM);}
if (write(fd, "Hello!\n", 7) != 7) // write
{error("%s: write error on file \"test.txt\"", argv[0]);}
lseek(fd, 0, SEEK_SET); // offset 0 from the beginning of file
if (write(fd, "Hello, ", 7) != 7) // overwrite
{error("%s: write error on file \"test.txt\"", argv[0]);}
// lseek(fd, 7, SEEK_SET); // offset 7 from the beginning of file
// lseek(fd, 0, SEEK_CUR); // offset 0 from the current position
// lseek(fd, 0, SEEK_END); // offset 0 from the end of file
if (write(fd, "world!\n", 7) != 7) // append
{error("%s: write error on file \"test.txt\"", argv[0]);}
close(fd);
if ((fd = open("test.txt", O_RDONLY, 0)) == -1) // 0 - permissions
{error("%s: can't open \"test.txt\"", argv[0]);}
if (get(fd, 0, buf, 7) == 7) // get first 7 chars (bytes)
{write(1, buf, 7);} // write to stdout (1)
if (get(fd, 7, buf, 7) == 7) // get more 7 chars (bytes)
{write(1, buf, 7);} // write to stdout (1)
close(fd);
unlink("test.txt"); // remove (delete) file
exit(0);
}
// read n bytes from position pos, return no of bytes read
int get (int fd, long pos, char *buf, int n)
{
if (lseek(fd, pos, SEEK_SET) >= 0)
{return read(fd, buf, n);}
// else
return -1;
}
#include <stdarg.h> // for va_list, va_start, va_end
// print an error message and die (end program)
void error(char *fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stderr, "Error: ");
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
va_end(args);
exit(1); // end program from outside main()
}
/*
gcc lseek.c -o lseek
./lseek
Hello, world!
*/
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
Exercise 8-2. Rewrite fopen() and _fillbuf() with fields instead of explicit bit operations. Compare code size and execution speed.
Exercise 8-3. Design and write _flushbuf(), fflush(), and fclose().
#include <string.h> // for strcpy(), strcat(), strlen()
#include <stdlib.h> // for exit(), malloc(), free(), gcvt()
#include <time.h> // for clock_t, clock(), CLOCKS_PER_SEC
#include <fcntl.h> // for open(), creat(), O_RDONLY, O_WRONLY
#include <unistd.h> // for read(), write(), close(), lseek()
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 _iobuf
{
int cnt; // count, characters left in the buffer
char *ptr; // pointer to next character position
char *base; // location of buffer
int 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
enum _flags
{ // bit positions 0, 1, 2, 3, 4 (from the end)
_READ = 01, // file open for reading
_WRITE = 02, // file open for writing
_UNBUF = 04, // file is unbuffered
_EOF = 010, // EOF has occurred on this file (have reached end of file)
_ERR = 020 // error occurred on this file (an error appeared)
};
FILE _iob[OPEN_MAX] = // definition
{
{0, (char *)0, (char *)0, _READ, 0}, // stdin
{0, (char *)0, (char *)0, _WRITE, 1}, // stdout
{0, (char *)0, (char *)0, _WRITE | _UNBUF, 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 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
// open file, return file pointer:
FILE * fopen(char *name, char *mode);
void filecopy(FILE *, FILE *);
int fflush(FILE *);
int fclose(FILE *);
void printn_iob(void); // print null values
int main(int argc, char *argv[])
{
// printn_iob();
FILE *fp;
char *prog = argv[0]; // program name
char buf[BUFSIZ];
clock_t start, end;
double time;
start = clock();
if (argc == 1) // no command-line args, just program name
{ // copy standard input to standard output
filecopy(stdin, stdout);
}
else
{
while (--argc > 0) // for all args except program name
{ // open file for reading
if ((fp = fopen(*++argv, "r")) == NULL)
{
strcpy(buf, prog); // (re)set
strcat(buf, ": can't open \"");
strcat(buf, *argv);
strcat(buf, "\"\n");
write(2, buf, strlen(buf)); // 2 - stderr
exit(1); // end program, signalling error
}
// else // fp != NULL, file open
filecopy(fp, stdout);
fclose(fp); // close file, free fp
}
}
if (ferror(stdout))
{
strcpy(buf, prog); // reset
strcat(buf, ": error writing stdout\n");
write(2, buf, strlen(buf)); // 2 - stderr
exit(2); // end program, signal other error code
}
end = clock();
time = ((double) (end - start)) / CLOCKS_PER_SEC;
write(2, "Execution time: ", 16);
gcvt(time, 6, buf); // double to string, precision 6
write(2, buf, strlen(buf));
write(2, "\n", 1); // write 1 char (byte) to 2 (stderr)
exit(0);
}
// 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 ((fp->flag & (_READ | _WRITE)) == 0)
{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;
fp->flag = (*mode == 'r') ? _READ : _WRITE;
// we do not consider read/write operations
return fp;
}
int _fillbuf(FILE *fp) // allocate and fill input buffer
{
int bufsize;
if ((fp->flag & (_READ | _EOF | _ERR)) != _READ)
{return EOF;} // we do not consider read/write operations
// else
bufsize = (fp->flag & _UNBUF) ? 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;}
else {fp->flag |= _ERR;}
fp->cnt = 0; // reset
return EOF;
}
return (unsigned char) *fp->ptr++;
}
int _flushbuf(int c, FILE *fp)
{
int bufsize;
if ((fp->flag & (_WRITE | _EOF | _ERR)) != _WRITE)
{return EOF;} // we do not consider read/write operations
// else
bufsize = (fp->flag & _UNBUF) ? 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;
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 = 0;
close(fp->fd);
fp->fd = 0; // initial value (after initialization)
}
void printn_iob(void)
{ // print null values after initialization
int i;
for (i = 0; i < OPEN_MAX; i++)
{ // write to stdout (1)
write(1, "(", 1);
write(1, (_iob[i].cnt == 0) ? "0" : "1", 1);
write(1, ", ", 2);
write(1, (_iob[i].ptr == NULL) ? "0" : "1", 1);
write(1, ", ", 2);
write(1, (_iob[i].base == NULL) ? "0" : "1", 1);
write(1, ", ", 2);
write(1, (_iob[i].flag == 0) ? "0" : "1", 1);
write(1, ", ", 2);
write(1, (_iob[i].fd == 0) ? "0" : "1", 1);
write(1, ")\n", 2);
}
}
/*
gcc stdio.c -o stdio
./stdio
Hello! // Enter
Hello!
What's up? // Enter
What's up?
// Ctrl^D in Linux, Ctrl^Z+Enter in Windows (EOF)
Execution time: 0.000135
./stdio stdio.c > copy.txt
Execution time: 0.001941
diff -s stdio.c copy.txt
// Files stdio.c and copy.txt are identical
meld stdio.c copy.txt
// Files are identical
./stdio me.txt > copy.txt
Execution time: 0.063545
./stdio me.txt me.txt me.txt me.txt me.txt > copy.txt
Execution time: 0.303623
rm copy.txt // clean
*/
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
#include <string.h> // for strcpy(), strcat(), strlen()
#include <stdlib.h> // for exit(), malloc(), free(), gcvt()
#include <time.h> // for clock_t, clock(), CLOCKS_PER_SEC
#include <fcntl.h> // for open(), creat(), O_RDONLY, O_WRONLY
#include <unistd.h> // for read(), write(), close(), lseek()
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 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
// open file, return file pointer:
FILE * fopen(char *name, char *mode);
void filecopy(FILE *, FILE *);
int fflush(FILE *);
int fclose(FILE *);
void printn_iob(void); // print null values
int main(int argc, char *argv[]) // bit fields variant
{
// printn_iob();
FILE *fp;
char *prog = argv[0]; // program name
char buf[BUFSIZ];
clock_t start, end;
double time;
start = clock();
if (argc == 1) // no command-line args, just program name
{ // copy standard input to standard output
filecopy(stdin, stdout);
}
else
{
while (--argc > 0) // for all args except program name
{ // open file for reading
if ((fp = fopen(*++argv, "r")) == NULL)
{
strcpy(buf, prog); // (re)set
strcat(buf, ": can't open \"");
strcat(buf, *argv);
strcat(buf, "\"\n");
write(2, buf, strlen(buf)); // 2 - stderr
exit(1); // end program, signalling error
}
// else // fp != NULL, file open
filecopy(fp, stdout);
fclose(fp); // close file, free fp
}
}
if (ferror(stdout))
{
strcpy(buf, prog); // reset
strcat(buf, ": error writing stdout\n");
write(2, buf, strlen(buf)); // 2 - stderr
exit(2); // end program, signal other error code
}
end = clock();
time = ((double) (end - start)) / CLOCKS_PER_SEC;
write(2, "Execution time: ", 16);
gcvt(time, 6, buf); // double to string, precision 6
write(2, buf, strlen(buf));
write(2, "\n", 1); // write 1 char (byte) to 2 (stderr)
exit(0);
}
// 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 (fp->flag._READ == 0 && fp->flag._WRITE == 0)
{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)
}
void printn_iob(void)
{ // print null values after initialization
int i;
for (i = 0; i < OPEN_MAX; i++)
{ // write to stdout (1)
write(1, "(", 1);
write(1, (_iob[i].cnt == 0) ? "0" : "1", 1);
write(1, ", ", 2);
write(1, (_iob[i].ptr == NULL) ? "0" : "1", 1);
write(1, ", ", 2);
write(1, (_iob[i].base == NULL) ? "0" : "1", 1);
write(1, ", (", 3);
write(1, (_iob[i].flag._READ == 0) ? "0" : "1", 1);
write(1, ",", 1);
write(1, (_iob[i].flag._WRITE == 0) ? "0" : "1", 1);
write(1, ",", 1);
write(1, (_iob[i].flag._UNBUF == 0) ? "0" : "1", 1);
write(1, ",", 1);
write(1, (_iob[i].flag._EOF == 0) ? "0" : "1", 1);
write(1, ",", 1);
write(1, (_iob[i].flag._ERR == 0) ? "0" : "1", 1);
write(1, "), ", 3);
write(1, (_iob[i].fd == 0) ? "0" : "1", 1);
write(1, ")\n", 2);
}
}
/*
gcc stdiob.c -o stdiob
./stdiob
Hello! // Enter
Hello!
What's up? // Enter
What's up?
// Ctrl^D in Linux, Ctrl^Z+Enter in Windows (EOF)
Execution time: 0.000142
./stdiob stdiob.c > copy.txt
Execution time: 0.001948
diff -s stdiob.c copy.txt
// Files stdiob.c and copy.txt are identical
meld stdiob.c copy.txt
// Files are identical
./stdiob me.txt > copy.txt
Execution time: 0.065332
./stdiob me.txt me.txt me.txt me.txt me.txt > copy.txt
Execution time: 0.309746
rm copy.txt // clean
*/
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
*****************************************************************************************
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.
#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!
*/