Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

fgets

7 views
Skip to first unread message

Andrew Clark

unread,
Sep 16, 2000, 3:00:00 AM9/16/00
to
people are always saying that gets() and scanf() are evil, so i now use
fgets() exclusively. but i don't know how to check for buffer overflow. i do
check to see if there is a trailing newline, might that be on the right
track? i.e. if there is no newline, then overflow occurred?

char* buf[100];
char* p;

fgets(buf, sizeof buf, stdin);
if ((p = strchr(buf, '\n')) != NULL)
{
*p = '\0';
puts("No overflow occurred);
}

else
puts("Overflow occurred");

thanks
clark

Bruce G. Stewart

unread,
Sep 16, 2000, 3:00:00 AM9/16/00
to

It can't overflow in the sense of writing beyond the end of the buffer.

If there is no newline, the line was too long for the buffer, and the
unread portion of the line is still out there to be read with the next
fgets.

Bruce G. Stewart

unread,
Sep 16, 2000, 3:00:00 AM9/16/00
to
"Bruce G. Stewart" wrote:
>
> Andrew Clark wrote:
> >
> > people are always saying that gets() and scanf() are evil, so i now use
> > fgets() exclusively.

> It can't overflow in the sense of writing beyond the end of the buffer.


>
> If there is no newline, the line was too long for the buffer, and the
> unread portion of the line is still out there to be read with the next
> fgets.

(Or the last "line" has no new-line, and the next call to fgets() will
return NULL.)

mike burrell

unread,
Sep 16, 2000, 3:00:00 AM9/16/00
to
Andrew Clark <anc...@syr.edu> wrote:
> people are always saying that gets() and scanf() are evil, so i now use
> fgets() exclusively. but i don't know how to check for buffer overflow. i do
> check to see if there is a trailing newline, might that be on the right
> track? i.e. if there is no newline, then overflow occurred?

yes, sadly, that's the only way to do it. it would be very worth your while
to make your own get_line() function which will continuously fgets(),
perhaps like:

#define INITIAL_BUFFER_SIZE 64

char *
get_line(FILE *f)
{
size_t l = INITIAL_BUFFER_SIZE / 2;
char *r = calloc(INITIAL_BUFFER_SIZE / 2, 1);

if (! r) you_re_really_in_trouble;

while (1) {
char lne[l];
size_t lne_len;

if (! fgets(l, sizeof l, f))
unexpected_EOF;
lne_len = strlen(l);

if (l[lne_len - 1] == '\n')
l[lne_len - 1] = 0;
else
break;

r = realloc(r, (l *= 2) * sizeof *r);
if (! r) out_of_memory;

strcpy(r, l);
}

return r;
}

it relies on (commonly used) C99 behaviour. it's probably slow. it's
definitely ugly. it's just to give you an idea of what you could do.

the point is, though, as you've found out, using fgets() just by itself is
very ugly. you have to check for the existance of a newline and then make
some decisions on what to do if there is an overflow, so i find this kind of
function very useful for reading in strings.

--
/"\ m i k e b u r r e l l
\ / ASCII RIBBON CAMPAIGN mik...@home.com
X AGAINST HTML MAIL,
/ \ AND NEWS TOO, dammit finger mik...@mikpos.dyndns.org for GPG key

Richard Heathfield

unread,
Sep 16, 2000, 3:00:00 AM9/16/00
to
Andrew Clark wrote:
>
> people are always saying that gets() and scanf() are evil, so i now use
> fgets() exclusively. but i don't know how to check for buffer overflow.

You don't have to, because fgets() is guaranteed not to overflow its
buffer (or, rather, it won't write more than the number of bytes you
tell it it can, via the second argument). Of course, what that means is
that it may not be able to collect a whole line of data in one go.

> i do
> check to see if there is a trailing newline, might that be on the right
> track? i.e. if there is no newline, then overflow occurred?

Close. If there's no newline, it means that there was more data than
would fit in the buffer, and so buffer overflow /would have/ occurred,
if you'd been using gets()!

What does fgets() do with that extra data? Nothing. It leaves it alone.
You can collect it, therefore, using another call to fgets(). This is
rather useful. Those people who say "fgets() is irritating because it
doesn't get rid of the '\n' character" haven't thought enough about just
how important it is that fgets() should /not/ do this.


--
Richard Heathfield
"Usenet is a strange place." - Dennis M Ritchie, 29 July 1999.
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
65 K&R Answers: http://users.powernet.co.uk/eton/kandr2/index.html (32
to go)

Andrew Clark

unread,
Sep 16, 2000, 3:00:00 AM9/16/00
to

"Richard Heathfield" <bin...@eton.powernet.co.uk> wrote in message
news:39C3E7C2...@eton.powernet.co.uk...

> Andrew Clark wrote:
> >
> > people are always saying that gets() and scanf() are evil, so i now use
> > fgets() exclusively. but i don't know how to check for buffer overflow.
>
> You don't have to, because fgets() is guaranteed not to overflow its
> buffer (or, rather, it won't write more than the number of bytes you
> tell it it can, via the second argument). Of course, what that means is
> that it may not be able to collect a whole line of data in one go.
>
> > i do
> > check to see if there is a trailing newline, might that be on the right
> > track? i.e. if there is no newline, then overflow occurred?
>
> Close. If there's no newline, it means that there was more data than
> would fit in the buffer, and so buffer overflow /would have/ occurred,
> if you'd been using gets()!

that's what i meant, of course. oops. not overflow in the sense that the
data would be written past the end of the buffer, but that the buffer would
be filled with data, and "overflow" would occur if there was data left on
stdin to be read.

Eli Bendersky

unread,
Sep 17, 2000, 3:00:00 AM9/17/00
to

Richard Heathfield wrote:

> Andrew Clark wrote:
> >
> > people are always saying that gets() and scanf() are evil, so i now use
> > fgets() exclusively. but i don't know how to check for buffer overflow.
>
> You don't have to, because fgets() is guaranteed not to overflow its
> buffer (or, rather, it won't write more than the number of bytes you
> tell it it can, via the second argument). Of course, what that means is
> that it may not be able to collect a whole line of data in one go.
>
> > i do
> > check to see if there is a trailing newline, might that be on the right
> > track? i.e. if there is no newline, then overflow occurred?
>
> Close. If there's no newline, it means that there was more data than
> would fit in the buffer, and so buffer overflow /would have/ occurred,
> if you'd been using gets()!
>

> What does fgets() do with that extra data? Nothing. It leaves it alone.
> You can collect it, therefore, using another call to fgets(). This is
> rather useful. Those people who say "fgets() is irritating because it
> doesn't get rid of the '\n' character" haven't thought enough about just
> how important it is that fgets() should /not/ do this.

What is it useful for ?

Richard Heathfield

unread,
Sep 17, 2000, 3:00:00 AM9/17/00
to
Eli Bendersky wrote:
>
> Richard Heathfield wrote:
>
> > Andrew Clark wrote:
> > >
> > > people are always saying that gets() and scanf() are evil, so i now use
> > > fgets() exclusively. but i don't know how to check for buffer overflow.
> >
> > You don't have to, because fgets() is guaranteed not to overflow its
> > buffer (or, rather, it won't write more than the number of bytes you
> > tell it it can, via the second argument). Of course, what that means is
> > that it may not be able to collect a whole line of data in one go.
> >
> > > i do
> > > check to see if there is a trailing newline, might that be on the right
> > > track? i.e. if there is no newline, then overflow occurred?
> >
> > Close. If there's no newline, it means that there was more data than
> > would fit in the buffer, and so buffer overflow /would have/ occurred,
> > if you'd been using gets()!
> >
> > What does fgets() do with that extra data? Nothing. It leaves it alone.
> > You can collect it, therefore, using another call to fgets(). This is
> > rather useful. Those people who say "fgets() is irritating because it
> > doesn't get rid of the '\n' character" haven't thought enough about just
> > how important it is that fgets() should /not/ do this.
>
> What is it useful for ?

It's useful for those situations where you are prepared to deal with
strings in bulk, but don't know in advance how long those strings will
be. Thus, consider a simple tee:

#include <stdio.h>

int main(int argc, char *argv[])
{
FILE *fp = NULL;
char buffer[80];

/* insert argc/argv checking code here */

fp = fopen(argv[1], "w");
if(fp != NULL)
{
while(fgets(buffer, sizeof buffer, stdin))
{
printf("%s", buffer); /* one of those times we don't want \n */
fprintf(fp, "%s, buffer); /* another of those times */
}
fflush(stdout);
fflush(fp);
fclose(fp);
}
else
{
fprintf(stderr, "Broken tee.\n");
}
return 0;
}

That was pretty trivial. Note the complete absence of newline handling.
Very elegant.

A more complex example might involve reading an entire text file into
memory. Here, it's useful to be able to read the file into memory in
one-line chunks, but we don't know in advance how long a line is, but
that's okay, because fgets() "paginates" the line for us in
easy-to-handle sections, which we can string together using realloc.

I won't show code here, but I've published that exact example recently
(hmm - does that count as a plug? If so, my apologies); Malc- 'gets()'
-olm might be interested to know that it is the very example of robust
fgets() that I wouldn't show him a few months back.

Chris Mears

unread,
Sep 17, 2000, 3:00:00 AM9/17/00
to
Eli Bendersky <spur...@yahoo.com> wrote:

>
>
>Richard Heathfield wrote:
>
>> Andrew Clark wrote:
>> >
>> > people are always saying that gets() and scanf() are evil, so i now use
>> > fgets() exclusively. but i don't know how to check for buffer overflow.
>>
>> You don't have to, because fgets() is guaranteed not to overflow its
>> buffer (or, rather, it won't write more than the number of bytes you
>> tell it it can, via the second argument). Of course, what that means is
>> that it may not be able to collect a whole line of data in one go.
>>
>> > i do
>> > check to see if there is a trailing newline, might that be on the right
>> > track? i.e. if there is no newline, then overflow occurred?
>>
>> Close. If there's no newline, it means that there was more data than
>> would fit in the buffer, and so buffer overflow /would have/ occurred,
>> if you'd been using gets()!
>>
>> What does fgets() do with that extra data? Nothing. It leaves it alone.
>> You can collect it, therefore, using another call to fgets(). This is
>> rather useful. Those people who say "fgets() is irritating because it
>> doesn't get rid of the '\n' character" haven't thought enough about just
>> how important it is that fgets() should /not/ do this.
>
>What is it useful for ?

(I assume you mean fgets()'s behaviour regarding the newline.)

Let's say we want to read some lines from a text file, and write them
somewhere else, while maintaining the original structure of the lines.

#include <stdio.h>
void foo(FILE *in, FILE *out)
{
char buffer[50];

while (fgets(buffer, sizeof buffer, in) != NULL) {
fputs(buffer, out);
}
}

Let's also say that there are some lines in the text file that are longer
that are longer that 49 characters. The call to fgets() will read the
first part, then it's written to the output stream. The next call to
fgets() will read the second part. Eventually it'll hit a newline, and the
newline will be printed with the buffer.

If fgets() discarded the newline, we'd never know whether an entire line
had been read, or just part of a line.


Bob Wightman

unread,
Sep 17, 2000, 3:00:00 AM9/17/00
to
In article <7KRw5.10824$Y7.2...@news1.rdc1.ab.home.com>, mike burrell
<mik...@home.com> writes

>Andrew Clark <anc...@syr.edu> wrote:
>> people are always saying that gets() and scanf() are evil, so i now use
>> fgets() exclusively. but i don't know how to check for buffer overflow. i do

>> check to see if there is a trailing newline, might that be on the right
>> track? i.e. if there is no newline, then overflow occurred?
>
>yes, sadly, that's the only way to do it. it would be very worth your while
>to make your own get_line() function which will continuously fgets(),
>perhaps like:
>
>#define INITIAL_BUFFER_SIZE 64
>
>char *
>get_line(FILE *f)
>{
> size_t l = INITIAL_BUFFER_SIZE / 2;
> char *r = calloc(INITIAL_BUFFER_SIZE / 2, 1);
char *q = NULL;

>
> if (! r) you_re_really_in_trouble;
>
> while (1) {
> char lne[l];
> size_t lne_len;
>
> if (! fgets(l, sizeof l, f))
> unexpected_EOF;
> lne_len = strlen(l);
>
> if (l[lne_len - 1] == '\n')
> l[lne_len - 1] = 0;
> else
> break;
>
> r = realloc(r, (l *= 2) * sizeof *r);
/* if realloc fails then you lose the existing input as
r is now NULL */

q = realloc(r, (l *= 2) * sizeof *r);
if(NULL == q)
{
/* error stuff here */
}
else
{
r = q;


}
> if (! r) out_of_memory;
>
> strcpy(r, l);
> }
>
> return r;
>}
>
>it relies on (commonly used) C99 behaviour. it's probably slow. it's
>definitely ugly. it's just to give you an idea of what you could do.
>
>the point is, though, as you've found out, using fgets() just by itself is
>very ugly. you have to check for the existance of a newline and then make
>some decisions on what to do if there is an overflow, so i find this kind of
>function very useful for reading in strings.
>

--

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Bob Wightman

Kolla

unread,
Sep 17, 2000, 3:00:00 AM9/17/00
to
>>>>> "mike" == mike burrell <mik...@home.com> writes:

<snip>
mike> char * get_line(FILE *f) { size_t l = INITIAL_BUFFER_SIZE /
mike> 2; char *r = calloc(INITIAL_BUFFER_SIZE / 2, 1);

mike> if (! r) you_re_really_in_trouble;

mike> while (1) { char lne[l]; size_t lne_len;

Isn't that l has to be replaced with lne in this loop??.

mike> if (! fgets(l, sizeof l, f)) unexpected_EOF;
mike> lne_len = strlen(l);

mike> if (l[lne_len - 1] == '\n') l[lne_len - 1] =
mike> 0; else break;

mike> r = realloc(r, (l *= 2) * sizeof *r); if (!
mike> r) out_of_memory;

mike> strcpy(r, l); }

mike> return r; }
<snip>
--
Kolla Suresh Babu
*Note*: Don't use my "Reply To:"
Use <kolla at mailcity dot com>

mike burrell

unread,
Sep 17, 2000, 3:00:00 AM9/17/00
to
Kolla <us...@domain.com> wrote:
>>>>>> "mike" == mike burrell <mik...@home.com> writes:
> Isn't that l has to be replaced with lne in this loop??.

yes. sorry.

Allin Cottrell

unread,
Sep 18, 2000, 3:00:00 AM9/18/00
to
Richard Heathfield wrote:

> fflush(fp);
> fclose(fp);

This is off the original question, but can one assume that
when one fcloses a stream it is automatically flushed (making
the above redundant), or is it good practice to include
the fflush explicitly?

Allin Cottrell.

Kaz Kylheku

unread,
Sep 18, 2000, 10:20:12 PM9/18/00
to

fclose not only flushes, but it has a return value which can indicate its
failure to flush the data. However, whether or not fclose() suceeds in
flushing, the stream is disposed of. Therefore if it is desirable to recover
in some way after a write error, such as by clearing the error indicator and
trying the operation again, then the explicit fflush() is a better choice.

--
Any hyperlinks appearing in this article were inserted by the unscrupulous
operators of a Usenet-to-web gateway, without obtaining the proper permission
of the author, who does not endorse any of the linked-to products or services.

Allin Cottrell

unread,
Sep 19, 2000, 3:00:00 AM9/19/00
to
Kaz Kylheku wrote:

> >This is off the original question, but can one assume that
> >when one fcloses a stream it is automatically flushed (making
> >the above redundant), or is it good practice to include
> >the fflush explicitly?
>
> fclose not only flushes, but it has a return value which can indicate its
> failure to flush the data. However, whether or not fclose() suceeds in
> flushing, the stream is disposed of. Therefore if it is desirable to recover
> in some way after a write error, such as by clearing the error indicator and
> trying the operation again, then the explicit fflush() is a better choice.

I think I see. But then would I be right in saying that

fflush(fp);
fclose(fp);

accomplishes nothing that is not accomplished by just

fclose(fp);

?

Allin Cottrell.

Chris Torek

unread,
Sep 20, 2000, 3:00:00 AM9/20/00
to
In article <39C80F76...@ricardo.ecn.wfu.edu>
Allin Cottrell <cott...@ricardo.ecn.wfu.edu> writes:
>... then would I be right in saying that

>
> fflush(fp);
> fclose(fp);
>
>accomplishes nothing that is not accomplished by just
>
> fclose(fp);
>
>?

Aside from being able to interrogate "fp" in between the two
operations, yes. I say "aside" because in the past I have used
code of the form:

... sequence of write operations on file "fp" ...
fflush(fp);
if (ferror(fp)) {
warn("%s: write error", filename);
status = FAIL;
}
fclose(fp);

to put in a single "something went wrong while writing a file"
test. (Although it more a comp.unix.programmer thing, this also
lets one write:

fflush(fp);
err2 = fsync(fileno(fp));
if (ferror(fp) || err2) warn ...
fclose(fp);

which not only forces data out of stdio, but also down onto the
underlying storage device. Then too, prior to ANSI C, it was not
entirely clear whether all implementations' fclose() functions
returned a valid error value.)
--
In-Real-Life: Chris Torek, Berkeley Software Design Inc
El Cerrito, CA, USA Domain: to...@bsdi.com +1 510 234 3167
http://claw.bsdi.com/torek/ (not always up) I report spam to abuse@.

Allin Cottrell

unread,
Sep 20, 2000, 3:00:00 AM9/20/00
to
Richard Heathfield wrote:

> I only put the fflush into that little code snippet because, if I
> hadn't, someone would have complained. So in it went and - oh look! -
> someone complained.
>
> Where's that bottle of Glenfiddich got to? Ah...

So it was a "little stream of whisky" that was being flushed?

Allin Cottrell.

Richard Heathfield

unread,
Sep 21, 2000, 3:00:00 AM9/21/00
to
Allin Cottrell wrote:
>
> Richard Heathfield wrote:
>
> > fflush(fp);
> > fclose(fp);
>
> This is off the original question, but can one assume that
> when one fcloses a stream it is automatically flushed (making
> the above redundant), or is it good practice to include
> the fflush explicitly?
>
> Allin Cottrell.

I only put the fflush into that little code snippet because, if I
hadn't, someone would have complained. So in it went and - oh look! -
someone complained.

Where's that bottle of Glenfiddich got to? Ah...


Your health, sir.


;-)

David Thompson

unread,
Sep 24, 2000, 3:00:00 AM9/24/00
to
mike burrell <mik...@home.com> wrote :
...

> to make your own get_line() function which will continuously fgets(),
> perhaps like:
...
In addition to the other comments (about variable names
and realloc failure), I think you want to break when '\n'
is *present* (not absent), and strcat() not strcpy()
the new stuff into your realloc'ed buffer.
Or strcpy(r+l,lne); Or memcpy(r+l,lne,lne_len+1);

> it relies on (commonly used) C99 behaviour. it's probably slow. it's
> definitely ugly. it's just to give you an idea of what you could do.
>

You can avoid the VLA, and the strcat() etc., by doing
realloc() first and then fgets'ing into the "fresh" part of it,
though you may have to do an N-and-a-half loop.
Or you can start out with a nulled pointer and use the feature
that realloc(null,n) does a malloc; this is either clever or devious
(or both?) depending on how you look at it.

--
- David.Thompson 1 now at worldnet.att.net


0 new messages