Exercises 8-2, 8-3 (stdio)

Chapter_8     Exercise_8-1     lseek Exercise_8-4







Exercises 8-2, 8-3     K&R, p. 178-179


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().




CONTENTS:     stdio.c     stdiob.c




stdio.c     K&R, p. 176-178         download


#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
*/





Note:  For the contents of text file me.txt see Martin_Eden on Project_Gutenberg.











stdiob.c     K&R, p. 150, 176-178         download


#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
*/





Note:  For the contents of text file me.txt see Martin_Eden on Project_Gutenberg.









Chapter_8     Exercise_8-1     lseek BACK_TO_TOP Exercise_8-4



Comments

Popular posts from this blog

Contents

Blogger Page Margins in Contempo