Home > 书山有路 > Something about setbuf

Something about setbuf

September 1st, 2011 Leave a comment Go to comments

When I worked on Exercise 5.1 of APUE.2e, I made some discoveries about function setbuf : void setbuf(FILE *restrict fp, char *restrict buf).

First, setbuf doesn’t check whether buf has a size of BUFSIZ. It leaves the work to users to make sure that buf has a size of BUFSIZ. I thought it would check!!!

Second, when the stream to be set is associated with a terminal device, such as stdin, stdout and stderr, setbuf doesn’t set them to line buffered (when buf is not NULL), but fully buffered. My system is Ubuntu 10.04 (2.6.32-33-generic).

Below is the program I worked out. A litter messy.

If you can’t view it, here is the link to download the source file: Click Me

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
 * exercise5-1.c
 *
 *  Created on: Sep 1, 2011
 *      Author: zhuhuang
 */
 
//How to build apueerr.h? I put the code of error.c from Figure B.3 and errorlog.c from Figure B.4 into a file named
//"apueerr.h". Line "#include <apue.h>" is added to the beginning of apueerr.h. Then place both apueerr.h and apue.h into
//folder /usr/include. Now you can used "#include <apueerr.h>" to replace "#include <apue.h>" in all programs from the book.
#include <apueerr.h>
 
//use isatty function to decide whether the file is a terminal device
 
//pr_stdio from fig5-11.c
void pr_stdio(const char *name, FILE *fp)
{
	printf("stream = %s, ", name);
 
	/*
	 * The following is nonportable.
	 */
	if(fp->_IO_file_flags & _IO_UNBUFFERED)
		printf("unbuffered");
	else if(fp->_IO_file_flags & _IO_LINE_BUF)
		printf("line buffered");
	else
		printf("fully buffered");
 
	printf(", buffer size = %dn", fp->_IO_buf_end - fp->_IO_buf_base);
}
 
//Based on testing, when buffer size if small than BUFSIZ, system-provided setbuf still works and will expand the size of
//buffer to BUFSIZ without moving buf. And even when the stream (such as stdin, stdout, stderr)is associated with a
//terminal device, it is not set to be line buffered, but fully buffered.
 
//My setbuf can do the same things, except that we have to move the buffer when its size is smaller than BUFSIZ.
//Realloc gives a different starting address when it is used to expand an existing buffer.
 
//The APUE2e book said "buf must point to a buffer of length BUFSIZ" (Page 137). However, if buf doesn't point to a buffer
//of BUFSIZ, setbuf still works. The only explanation I can come up with is that: setbuf assumes the provided buf is of size
//BUFSIZ and doesn't check it (It seems that there is no way to check the size of dynamically allocated buffer). It then
//simply make 'buf' to be buffer for fp. It leaves the work to the user to make sure the allocated size of buf is BUFSIZ.
/*
void setbuf(FILE *fp, char *buf)
{
	//char *newbuf;
	printf("buf address before realloc: %#xn", buf);
	if(buf == NULL)
	{
		if(setvbuf(fp, NULL, _IONBF, 0) != 0) //Disable buffering
			err_sys("setvbuf error 1");
	}
	else
	{
		//sizeof(buf) doesn't give the size that buf has been allocated to.
		//No way to check the size.
		if(sizeof(buf) < BUFSIZ)
			if((buf = realloc(buf, BUFSIZ)) == NULL)
				err_sys("realloc error");
		printf("buf address after realloc: %#xn", buf);
		if(setvbuf(fp, buf, _IOFBF, BUFSIZ) != 0) //Realloc succeed. Make it fully buffered.
			err_sys("setvbuf error 2");
	}
}
*/
 
//Another version of setbuf, which don't check the size of buf.
//This one works exactly like the system-provided setbuf, judged from the output.
void setbuf(FILE *fp, char *buf)
{
	//char *newbuf;
	printf("buf address before realloc: %#xn", buf);
	if(buf == NULL)
	{
		if(setvbuf(fp, NULL, _IONBF, 0) != 0) //Disable buffering
			err_sys("setvbuf error 1");
	}
	else
	{
		if(setvbuf(fp, buf, _IOFBF, BUFSIZ) != 0) //Realloc succeed. Make it fully buffered.
			err_sys("setvbuf error 2");
	}
}
 
int main(void)
{
	//char buffer1[20]; //Using this form will cause realloc to fail on buffer1.
	//char buffer2[20]; //Using this form will cause realloc to fail on buffer2.
	char *buffer1 = malloc(20);
	char *buffer2 = malloc(20);
 
	//For testing
	//char *buffer3 = malloc(20);
	//printf("size of buffer3; %dn", sizeof(buffer3)); //gives 4.
	//buffer3 = realloc(buffer3, BUFSIZ);
	//printf("size of buffer3; %dn", sizeof(buffer3)); //gives 4.
 
	printf("Starting address of char array buffer1: %#xn", buffer1);
	//printf("Ending address of char array buffer1: %#xn", &buffer1[19]);
	printf("Starting address of char array buffer2: %#xn", buffer2);
	//printf("Ending address of char array buffer2: %#xn", &buffer2[19]);
 
	//Perform I/O on stream: stdin, stdout, stderr
	fputs("enter any charactern", stdout);
	if(getchar() == EOF)
		err_sys("getchar error");
	fputs("one line to standard errorn", stderr);
 
	printf("Default size for setbuf (BUFSIZ): %dn", BUFSIZ);
 
	printf("Before calling setbuf to stdin, stdout and stderr: n");
	pr_stdio("stdin", stdin);
	pr_stdio("stdout", stdout);
	pr_stdio("stdout", stderr);
 
	setbuf(stdin, buffer1); //it doesn't matter that the buffer size is not BUFSIZ
	//printf("Starting address of char array buffer1: %#xn", buffer1);
	//printf("Ending address of char array buffer1: %#xn", &buffer1[BUFSIZ-1]);
	printf("Starting address of the buffer made by setbuf for stdin: %#xn", stdin->_IO_buf_base);
	printf("Ending address of the buffer made by setbuf for stdin: %#xn", stdin->_IO_buf_end);
	setbuf(stdout, NULL);
	setbuf(stderr, buffer2);
	//printf("Starting address of char array buffer2: %#xn", buffer2);
	//printf("Ending address of char array buffer2: %#xn", &buffer2[BUFSIZ-1]);
	printf("Starting address of the buffer made by setbuf for stderr: %#xn", stderr->_IO_buf_base);
	printf("Ending address of the buffer made by setbuf for stderr: %#xn", stderr->_IO_buf_end);
 
	printf("After calling setbuf to stdin, stdout and stderr: n");
	pr_stdio("stdin", stdin);
	pr_stdio("stdout", stdout);
	pr_stdio("stdout", stderr);
}
Categories: 书山有路 Tags: , , , ,
  1. September 1st, 2011 at 04:11 | #1

    底层的东西,看不懂。

    搞IT的,果然是大半夜码代码。

  2. September 2nd, 2011 at 17:06 | #2

    @ygc 这个应该算系统层的,系统调用这些,还不算太底层。

  1. No trackbacks yet.