old-www/LDP/LG/issue24/rogers.html

550 lines
27 KiB
HTML

<!--startcut ==========================================================-->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<title>The Standard C Library for Linux Issue 24</title>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#A000A0"
ALINK="#FF0000">
<!--endcut ============================================================-->
<H4>
"Linux Gazette...<I>making Linux just a little more fun!</I>"
</H4>
<P> <HR> <P>
<!--===================================================================-->
<center>
<H2>The Standard C Library for Linux, Part one: &lt;stdio.h> file functions</H2>
<H4>By <a href="mailto:jrogers@acmewidgets.com">James M. Rogers</a></H4>
</center>
<P> <HR> <P>
C is a very small language.&nbsp; This is a good thing.&nbsp; C programmers
will use nearly the entire C language every time they write a fair sized
program.&nbsp; The standard C library extends the functionality of C in
a way that is predictable on multiple systems.&nbsp; The library gives
us tools like scanf() and printf() that make reading and writing formatted
output much easier than working with blocks of characters using read()
and write().&nbsp; Also when you move from one C programming environment
to another, the functionality of printf() will be the same.&nbsp; You don't
have to relearn how to print formatted output every time you change machines.
<P>In this series of articles I will discuss the tools that are available
for the programmers in the standard C library.&nbsp; At the end is a bibliography
of the books and articles that I used to get this information.&nbsp; I
refer to these books and magazines on a daily basis when I program.&nbsp;
If you want to work as a C programmer I strongly recommend that you buy
and read these books and magazines.
<P>Many times the standard functions are overlooked and reinvented by programmers
(including myself!) to do things like seeing if a character is a letter
by:
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (c=>'a'
|| c&lt;='z' &amp;&amp; c=>'A' || c&lt;='Z')
<P>instead of using
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isalpha(c);
<P>The second form is much easier to read and figure out.&nbsp; There is
another reason to use the second form.&nbsp; The first example only works
for ASCII character sets, the second will work on _any_ machine.&nbsp;
If you want to write portable code (code that can be compiled and ran on
any machine with very minor changes) then use the standard C library.
<P>Several times in the past I have written code that took time to write
and debug and interface to the rest of my program only to discover that
there was already a function that did what I wanted to do.&nbsp; A few
months ago I was writing my own Multi User Dimension (MUD), based on a
client/server article in Linux Journal, and I needed to process what the
user had entered, one word at a time.&nbsp; So I wrote my own string tokenizer.&nbsp;
Turns out I could have used the strtok() function to do almost the exact
same thing.&nbsp; And other people will know what the strtok() function
does without having to decipher my code.
<P>Make your life easier, use the standard C library.&nbsp; It will also
help all of us who try to update and maintain your code later.
<P>The GNU compiler, gcc, comes with the GNU standard C library.&nbsp;
This compiler is one of the best in the world and the GNU standard C library
conforms almost exactly to the standard.&nbsp; In the places where the
standard is imprecise, you can expect very reasonable behavior from both
the compiler and the library.&nbsp; I am going to discuss the GNU standard
C library in these articles.
<P>The &lt;stdio.h> library handles the standard input and output functions
for C programmers.&nbsp; It is also by far the largest library.&nbsp; Because
the library is so large I am going to group the commands in these sections:&nbsp;
file operations, input and output.
<P>Now before we talk about files we need to agree on the words that we
are going to use.&nbsp; In Linux a file or device is considered to be a
<B>stream</B> of data.&nbsp; This stream of data that is associated with
a file or hardware device is accessed by your program <B>opening</B> the
file or device.&nbsp; Once the stream is opened then you can <B>read</B>
and/or <B>write</B> to it.
<P>Three streams are opened automatically when you execute a program.&nbsp;
Standard input (<B>stdin</B>), standard output (<B>stdout</B>), and standard
error (<B>stderr</B>).&nbsp; These can all be redirected by your shell
when you run the program but normally stdin is your keyboard and stdout
and stderr both go to your monitor.
<P>After you are done with your streams you need to tell the operating
system to clean up buffers and finish saving data to the devices.&nbsp;
You do this by <B>closing</B> the stream.&nbsp; If you don't close your
stream then it is possible to lose data.&nbsp; stdin, stdout and stderr
are all closed automatically the same way they are opened automatically.
<P>One of the most important things to remember when dealing with devices
and files is that you are dealing with the real world.&nbsp; <I>Don't assume
that the function is going to work.</I>&nbsp; Evan something like printf
can fail. Disks fill up or occasionally fail, users input the wrong data,
processors get too busy, other programs have your files locked.&nbsp; Murphy's
Law is in full effect when it comes to computer systems. Every function
that deals with the real world returns an error condition if the function
failed.&nbsp; Always check every return value and take the appropriate
action when there is an error condition.&nbsp; <I>Exceptions are not errors
unless they are handled badly.</I> <I>Exceptions are opportunities for
extra computation</I> (William Kahan, on exception handling.)
<P>The first example is to basically show how to open a file for reading.&nbsp;
It just dumps a file called test in the current directory to the standard
out.&nbsp; All exceptions are reported to standard error and then program
halted is with an error return.&nbsp; It should produce an error if a file
called test doesn't exist.
<P>--------------------------------------------------------
<BR><TT>#include &lt;stdio.h>&nbsp;&nbsp;&nbsp; /* this is a compiler directive
that tells the</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
the compiler that you are going to be using</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
functions that are in the standard input /</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
output library</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
*/</TT>
<BR><TT>main (){</TT>
<P><TT>/* declare variables */</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; FILE *stream;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
/* need a pointer to FILE for the stream */</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; int buffer_character;&nbsp;&nbsp;&nbsp; /* need
an int to hold a single character */</TT>
<P><TT>/* open the file called test for reading in the current directory
*/</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; stream = fopen("test", "r");</TT>
<P><TT>/* if the file wasn't opened correctly than the stream will be</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; equal to NULL.&nbsp; It is now customary to
represent NULL by casting</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; the value of 0 to the correct type yourself
rather than having the</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; compiler guess at the type of NULL to use.</TT>
<BR><TT>*/</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; if (stream == (FILE *)0) {</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf(stderr, "Error
opening file (printed to standard error)\n");</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit (1);</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp; /* end if */</TT>
<P><TT>/* read and write the file one character at a time until you reach</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; end-of-file on either our file or output.&nbsp;
If the EOF is on file_descriptor</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; then drop out of the while loop. if the end-of-file
is on report write</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; errors to standard out and exit the program
with an error condition</TT>
<BR><TT>*/</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; while ((buffer_character=getc(stream))!=EOF)
{</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* write the character
to standard out and check for errors */</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if((putc(buffer_character,
stdout)) == EOF) {</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
fprintf(stderr,"Error writing to standard out.&nbsp; (printed to standard
error)\n");</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
fclose(stream);</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
exit(1);</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
}&nbsp; /* end if */</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp; /* end while
*/</TT>
<P><TT>/* close the file after you are done with it, if file doesn't close
then report and exit */</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; if ((fclose(stream)) == EOF) {</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf(stderr,"Error
closing stream.&nbsp; (printed to standard error)\n");</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(1);</TT>
<BR><TT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp; /* end if */</TT>
<P><TT>/* report success back to environment */</TT>
<BR><TT>&nbsp;&nbsp;&nbsp; return 0;</TT>
<P><TT>}&nbsp; /* end main*/</TT>
<BR>-------------------------------------------------------------
<BR>&nbsp;
<BR>The above simple program is an example of&nbsp; opening a file, reading
the file, and then closing the file while also using stdout, and stderr.&nbsp;
I cut and pasted the code to a vi session and then saved, compiled, and
ran the program.
<P>What follows is a quick summary of the <B>file operations</B> in the
&lt;stdio.h> library.&nbsp; These are the operations that work directly
with streams.
<H4>
Opening Streams</H4>
Before a stream can be used you must <B>associate</B> the stream with some
device or file.&nbsp; This is called opening the stream.&nbsp; Your program
is asking for permission from the operating system to read or write to
a device.&nbsp; If you have the correct permissions, the file exists or
you can create the file and no-one else has the file locked then the operating
system allows you to open the file and gives you back an object that is
the stream.&nbsp; Using this object you can read and write to the stream
and when you are done you can close the stream.
<P>Let me discribe the format of the descriptions that you will see here
and in the man pages.&nbsp; The first entry is the type that is returned
by the function call.&nbsp; The second part is the function name itself
and the third part is the list of variable types that the function takes
for arguments.
<P>Looking at the first line below we see that the fopen function takes
two pointers to strings, one is a path to a file and the other is the open
mode of the program.&nbsp; The function will return a pointer to FILE type
which is a complex object that is defined in the &lt;stdio.h> library.
So in order to accept the return type you must have declared a variable
of type pointer to FILE, like the stream variable in the example above
on line 9.&nbsp; On line 13 of the example you can see where I call the
function fopen with the static filename of "test" and a mode of "r" and
then accept the return value into the stream object.
<P>A stream can be opened by any of these three functions:
<P><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; FILE&nbsp;&nbsp; *fopen( char *path,
char *mode)</FONT></TT>
<BR><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; FILE&nbsp; *fdopen( int fildes,
char *mode)</FONT></TT>
<BR><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; FILE *freopen( char *path, char
*mode, FILE *stream)</FONT></TT>
<P><TT><FONT SIZE=+1>char *path</FONT></TT> is a pointer to a string with
the filename in it.
<BR><TT><FONT SIZE=+1>char *mode</FONT></TT> is the mode of opening the
file (table follows.)
<BR><TT><FONT SIZE=+1>int fildes</FONT></TT> is a file descriptor which
has already been opened and whose mode matches.
<UL>You can get a file descriptor with the UNIX system function <B>open</B>.&nbsp;
Please note that a file descriptor is not a pointer to FILE.&nbsp; You
cannot close(stream), you must fclose(stream).&nbsp; This is a very hard
error to find if your compiler doesn't warn you about it.&nbsp; If you
are interested in Linux System calls type `man 2 intro`&nbsp; for an introduction
to the functions and what they do.</UL>
<TT><FONT SIZE=+1>FILE *stream</FONT></TT> is an already existing stream.
<P>These functions return a pointer to FILE type that represents the data
stream or a NULL of type <TT><FONT SIZE=+1>(FILE *)0 </FONT></TT>on any
error condition.
<P><B>fopen</B> is used to open the given filename with the respective
mode.&nbsp; This is the function that is used the most to open files.
<P><B>fdopen</B> is used to assign a stream to a currently opened file
descriptor.&nbsp; The file descriptor mode and the fdopen mode must match.
<P><B>freopen</B> is normally used redirect stdin, stdout and stderr to
file.&nbsp; The stream that is given will be closed and a new stream opened
to the given path with the given mode.
<P>This table shows the modes and their results:
<P><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; open stream for&nbsp;
truncate create starting</FONT></TT>
<BR><TT><FONT SIZE=+1>mode&nbsp;&nbsp; read&nbsp;&nbsp;&nbsp; write&nbsp;&nbsp;&nbsp;
file&nbsp;&nbsp;&nbsp;&nbsp; file&nbsp;&nbsp; position</FONT></TT>
<BR><TT><FONT SIZE=+1>----&nbsp;&nbsp; ----&nbsp;&nbsp;&nbsp; -----&nbsp;&nbsp;&nbsp;
----&nbsp;&nbsp;&nbsp;&nbsp; ----&nbsp;&nbsp; --------</FONT></TT>
<BR><TT><FONT SIZE=+1>"r"&nbsp;&nbsp;&nbsp;&nbsp; y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
n&nbsp;&nbsp;&nbsp;&nbsp; beginning</FONT></TT>
<BR><TT><FONT SIZE=+1>"r+"&nbsp;&nbsp;&nbsp; y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
n&nbsp;&nbsp;&nbsp;&nbsp; beginning</FONT></TT>
<BR><TT><FONT SIZE=+1>"w"&nbsp;&nbsp;&nbsp;&nbsp; n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
y&nbsp;&nbsp;&nbsp;&nbsp; beginning</FONT></TT>
<BR><TT><FONT SIZE=+1>"w+"&nbsp;&nbsp;&nbsp; y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
y&nbsp;&nbsp;&nbsp;&nbsp; beginning</FONT></TT>
<BR><TT><FONT SIZE=+1>"a"&nbsp;&nbsp;&nbsp;&nbsp; n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
y&nbsp;&nbsp;&nbsp;&nbsp; end-of-file</FONT></TT>
<BR><TT><FONT SIZE=+1>"a+"&nbsp;&nbsp;&nbsp; y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
y&nbsp;&nbsp;&nbsp;&nbsp; end-of-file</FONT></TT>
<P>To read the first line, "r" will open a stream for read, the stream
will not be opened for write, will not truncate the file to zero length,
will not create the file if it doesn't already exist and will be positioned
at the beginning of the stream.
<H4>
Stream Flushing</H4>
Sometimes you want your program to ensure that what you have written to
a file has actually gone to the disk and is not waiting in the buffer.
Or you might want to throw out a lot of user input and get fresh input,
for a game.&nbsp; The following two functions are useful for emptying the
streams buffers, though one just throws the data away while the other stores
it safely on to the stream.
<P><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; int fflush(FILE *stream)</FONT></TT>
<BR><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; int fpurge(FILE *stream)</FONT></TT>
<P><TT><FONT SIZE=+1>FILE *stream</FONT></TT> is an already existing stream.
<P>These functions return a 0 on success.&nbsp; On a failure they return
an EOF.
<P><B>fflush</B> is used to write out the buffers of the stream to a device
or file.
<P><B>fpurge</B> is used to clear the buffers of unwritten or unread data
that is in a buffer waiting.&nbsp; I think of this as a destructive purge
because it clears the read and write buffers by dumping the contents.
<H4>
Closing Streams</H4>
When you are done with a stream you must clean up after your program.&nbsp;
When you close a stream the command ensures that the buffers are successfully
written and that the stream is truly closed.&nbsp; If you just exit a program
without closing your files then more than likely the last few bytes that
you wrote will be there.&nbsp; But you won't know unless you check.&nbsp;
Also there is a limit to how many streams a single process can have open
at one time. So if you keep on opening streams without closing the old
streams you will use up system resources.&nbsp; Only one command is used
to close any stream.
<P><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; int fclose(FILE *stream)</FONT></TT>
<P><TT><FONT SIZE=+1>FILE *stream</FONT></TT> is an already existing stream.
<P>Returns a 0 on success, or an EOF otherwise.
<P><B>fclose</B> flushes the given streams buffers and then disassociates
the stream from the pointer to FILE.
<H4>
Renaming and Removing Files</H4>
These two commands work just like rm and mv, but without the options.&nbsp;
They are not recursive but your programs can be so watch that you don't
accidentally build your own version of <TT><FONT SIZE=+1>rm -rf / </FONT></TT>&lt;&lt;&lt;by
they way don't type this, it would delete your entire harddrive!!>>>
<P><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp; remove(char
*path)</FONT></TT>
<BR><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp; rename(char
*oldpath, const char *newpath)</FONT></TT>
<P><TT><FONT SIZE=+1>char *path, oldpath and newpath</FONT></TT> are all
pointers to existing files.
<P>Returns a 0 on success and a non-zero otherwise.
<P><B>remove</B> works just like rm to remove the file in the string pointed
to by path.
<P><B>rename</B> works just like move to rename a file from oldpath to
newpath, changing directories if need be.
<H4>
Temporary Files</H4>
You can create your own temp files by using the following functions:
<P><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; FILE *tmpfile(void)</FONT></TT>
<P>This command returns a pointer to a FILE of stream which is a temp file
that magically goes away when your program is done running.&nbsp; You never
even know the files name.&nbsp; If the function fails it returns a NULL
pointer of type (FILE *)0.
<P><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; char&nbsp; *tmpnam(char *string)</FONT></TT>
<P>This function returns a filename in the tmp directory that is unique,
or a NULL if there is an error.&nbsp; Each additional call overrides the
previous name so you must move the name somewhere else if you need to know
the name after you open the file.
<H4>
Stream Buffering</H4>
Normally a stream is block buffered, unless it is connected to a terminal
like stdin or stdout.&nbsp; In block buffered mode the stream reads ahead
a set amount a and then gives you what the input that you ask for as you
ask for it.&nbsp; Sometimes you want this to be bigger or smaller to improve
performance in some program.&nbsp; The following four functions can be
used to set the buffering type and the size of the buffers.&nbsp; The defaults
are normally pretty good so you shouldn't have to worry too much about
these.
<P><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp; setbuf(
FILE *stream, char *buf);</FONT></TT>
<BR><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; int&nbsp; setbuffer( FILE *stream,
char *buf, size_t size);</FONT></TT>
<BR><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; int setlinebuf( FILE *stream);</FONT></TT>
<BR><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp; setvbuf(
FILE *stream, char *buf, int mode , size_t size);</FONT></TT>
<P>Where mode is one of the following:
<BR><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; _IONBF</FONT></TT> unbuffered,
output sent as soon as received.
<BR><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; _IOLBF</FONT></TT> line buffered,
output sent as soon as a newline is received.
<BR><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; _IOFBF</FONT></TT> fully buffered,
output isn't sent until size characters are received.
<P><B>setbuf</B> is an alias for&nbsp;&nbsp;&nbsp;&nbsp; <TT><FONT SIZE=+1>setvbuf(stream,&nbsp;
buf,&nbsp; buf&nbsp; ?&nbsp; _IOFBF&nbsp;&nbsp; :&nbsp;&nbsp; _IONBF, BUFSIZ);</FONT></TT>
<BR><B>setbuffer</B> is an alias for&nbsp; <TT><FONT SIZE=+1>setvbuf(stream,&nbsp;
buf,&nbsp; buf&nbsp; ?&nbsp; _IOFBF&nbsp;&nbsp; :&nbsp;&nbsp; _IONBF, size);</FONT></TT>
<BR><B>setlinebuf</B> is an alias for <TT><FONT SIZE=+1>setvbuf(stream,
(char *)NULL, _IOLBF, 0);</FONT></TT>
<P><B>setvbuf</B> sets a buffer for the given stream of size_t size and
of buffer mode.
<H4>
Stream Posistioning</H4>
Once you open a stream you are located at a certain postition depending
on what mode you opened the stream in, as you read or write your position
increases with each character.&nbsp; You can see where you are at in the
stream and jump to any position in the stream.&nbsp; If you are writing
a database program you don't want to have to read and ignore a million
characters to get to the record that you want, you want to be able to jump
right to the record and start reading.
<P>Note that terminals cannot have their stream repositioned, only block
devices (like hard drives) will allow this.
<P>Also note that if you open a file for writing and use fseek to go out
10,000 bytes, write one character and then close the file that you will
not have a file of 10,001 bytes.&nbsp; The file will be much smaller.&nbsp;
This is called a sparse file.&nbsp; If you move a sparse file using the
mv command it will not change size because a mv is only a change to the
directory structure, not the file.&nbsp; If you cp or tar a sparse file
then it will expand out to its true size.
<P><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp; fseek( FILE *stream,
long offset, int whence);</FONT></TT>
<BR><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; long&nbsp; ftell( FILE *stream);</FONT></TT>
<BR><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; void rewind( FILE *stream);</FONT></TT>
<BR><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; int fgetpos( FILE *stream, fpos_t
*pos);</FONT></TT>
<BR><TT><FONT SIZE=+1>&nbsp;&nbsp;&nbsp; int fsetpos( FILE *stream, fpos_t
*pos);</FONT></TT>
<P><TT><FONT SIZE=+1>FILE *stream</FONT></TT> is a pointer to an already
existing stream.
<BR><TT><FONT SIZE=+1>long offset</FONT></TT> is added to the position
indicated by the whence.
<BR><TT><FONT SIZE=+1>int whence </FONT></TT>is<TT><FONT SIZE=+1> SEEK_SET,
SEEK_CUR, or SEEK_END </FONT></TT>depending on where you want the offset
to be applied to:&nbsp; the beginning, the current position or the end.
<BR><TT><FONT SIZE=+1>fpos_t *pos</FONT></TT> is a complex file position
indicator.&nbsp; On some systems you must use this to get and set stream
positions.
<BR>&nbsp;
<BR>If these functions are successful <B>fgetpos, fseek, fsetpos</B> return&nbsp;
0,&nbsp; and&nbsp; <B>ftell</B> returns the current offset.&nbsp; Otherwise,
EOF is returned.&nbsp; There is no return value from <B>rewind</B>.
<P><B>fseek</B> sets the file position in the stream to the value of offset
plus the position indicated by the whence, either the begginning, the current
or the end of file to get the new position in the stream.&nbsp; This is
useful for reading along, adding something to the end of the stream and
then going back to reading the stream where you left off.
<P><B>ftell</B> returns the current position of the stream.
<P><B>rewind</B> sets the current position to the beginning of the stream.&nbsp;
Notice that no error code is returned.&nbsp; This function is assumed to
always suceed.
<P><B>fgetpos</B> is used like ftell to return the position of the stream.&nbsp;
The position is returned in the pos variable which is of type fpos_t.
<P><B>fsetpos</B> is used like fseek in that it will set the current postion
of the stream to the value in pos.
<P>On some systems you have to use <B>fgetpos</B> and <B>fsetpos</B> in
order to reliably position your stream.
<H4>
Error Codes</H4>
When any of the above functions return an error you can see what the error
was and even get a text error message to display for the user.&nbsp; There
are a group of functions that deal with error values.&nbsp; It is enough
for now to be able to see that you have an errors and stop.
<P>However, if you write a nice GUI word processor you don't want the program
to stop everytime it can't open a file.&nbsp; You want it to display the
error message to the user and continue.&nbsp; In a future article I will
deal this error code functions, or someone else can summarize them for
us and send in an article and some commented source code to show us how
it's done.
<P>If anyone is interested the functions are: <B>clearerr, feof, ferror,
and fileno.</B>
<H4>
Conclusion</H4>
Well that's enough for this month.&nbsp; I have learned a lot and I hope
you have as well.&nbsp; Most of this information is available through man
page system but the dates on these are 4 years old.&nbsp; If anyone has
updates on any of this information please send it to me and I will correct
myself in further articles.
<P>Next month I want to talk about input and output.&nbsp; I will take
the simple program above and add some functionallity to it to add a column
of numbers and output the results to standard out.&nbsp; Hopefully this
example program can grow into something useful.
<H4>
Bibilography:</H4>
<I>The ANSI C Programming Language, Second Edition</I>, Brian W. Kernighan,
Dennis M. Ritchie, Printice Hall Software Series, 1988
<P><I>The Standard C Library</I>, P. J. Plauger, Printice Hall P T R, 1992
<P><I>The Standard C Library, Parts 1, 2, and 3</I>, Chuck Allison, <I>C/C++
Users Journal</I>, January, February, March 1995
<P>STDIO(3), BSD MANPAGE, <I>Linux Programmer's Manual</I>, 29 November
1993
<P><I>Unidentified File Objects</I>, Lisa Lees, <I>Sys Admin</I>, July/August
1995
<P><I>A Conversation With William Kahan</I>, Jack Woehr, <I>Dr Dobb's Journal</I>,
November 1997<I></I>
<P><I>Java and Client-Server</I>, Joe Novosel, <I>Linux Journal</I>, January
1997
<!--===================================================================-->
<P> <hr> <P>
<center><H5>Copyright &copy; 1998, James Rogers <BR>
Published in Issue 24 of <i>Linux Gazette</i>, January 1998</H5></center>
<!--===================================================================-->
<P> <hr> <P>
<A HREF="./index.html"><IMG ALIGN=BOTTOM SRC="../gx/indexnew.gif"
ALT="[ TABLE OF CONTENTS ]"></A>
<A HREF="../index.html"><IMG ALIGN=BOTTOM SRC="../gx/homenew.gif"
ALT="[ FRONT PAGE ]"></A>
<A HREF="./berglund.html"><IMG SRC="../gx/back2.gif"
ALT=" Back "></A>
<A HREF="./zawinski.html"><IMG SRC="../gx/fwd.gif" ALT=" Next "></A>
<P> <hr> <P>
<!--startcut ==========================================================-->
</BODY>
</HTML>
<!--endcut ============================================================-->