[comp.lang.c] Substitute for gets

xmjschm@hscfvax.harvard.edu (MJSchmelzer) (02/08/89)

Given that gets() is unsafe because it doesn't check its input,
what form of scanf() should be used?

The problem I'm having is this:

scanf("%s", buffer);

where buffer is char[255] and the scanf statement is in a loop,
the \n gets passed to the next scanf call. 
Another problem is if I enter, say:

foo bar

to this scanf-in-a-loop, it cycles through the stdin buffer twice to yield:

foo
bar

I know I'm not saying this very well, sorry. But I figure this is a pretty
basic dilemma that most folks have encountered. I mean, gets() in such a 
loop does just fine, ie it would return "foo bar", but I have yet to
kludge up a functionally equivalent scanf().

Does this mean gets() is indispensable ? 
Thanks for any help/insights.
 
-- 
==============  xmjschm@harvspha.BITNET ============= 
Mike Schmelzer  xmjschm@hscfvax.harvard.edu           Quotes are stupid.
=====================================================

barmar@think.COM (Barry Margolin) (02/09/89)

In article <721@hscfvax.harvard.edu> xmjschm@hscfvax.harvard.edu (MJSchmelzer) writes:
[Describes problems using scanf() in place of gets().]
>Does this mean gets() is indispensable ? 

I don't think so.  The simplest, safe replacement for gets() is
fgets().  You'll have to specify stdin explicitly, and you'll have to
strip off the trailing newline if you don't want it, but otherwise
it's just what you want.

Barry Margolin
Thinking Machines Corp.

barmar@think.com
{uunet,harvard}!think!barmar

henry@utzoo.uucp (Henry Spencer) (02/09/89)

In article <721@hscfvax.harvard.edu> xmjschm@hscfvax.harvard.edu (MJSchmelzer) writes:
>Given that gets() is unsafe because it doesn't check its input,
>what form of scanf() should be used?

The replacement for gets() is fgets(), not scanf().  In general it is both
safer and more satisfactory to read a line into a buffer with fgets() and
then pick it apart by whatever means is appropriate, which might possibly
include sscanf() although it isn't really satisfactory, than to try to use
scanf() directly on the input.  Printf() was an experiment that worked; the
same cannot be said of scanf(), at least not with anywhere near the same
level of enthusiasm.
-- 
Allegedly heard aboard Mir: "A |     Henry Spencer at U of Toronto Zoology
toast to comrade Van Allen!!"  | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

jeenglis@nunki.usc.edu (Joe English) (02/09/89)

xmjschm@hscfvax.harvard.edu (MJSchmelzer) writes:
>Given that gets() is unsafe because it doesn't check its input,
>what form of scanf() should be used?
[...]
>Another problem is if I enter, say:
>foo bar
>to this scanf-in-a-loop, it cycles through the stdin buffer twice to yield:
>foo
>bar
>I know I'm not saying this very well, sorry. But I figure this is a pretty
>basic dilemma that most folks have encountered. I mean, gets() in such a 
>loop does just fine, ie it would return "foo bar", but I have yet to
>kludge up a functionally equivalent scanf().


The truth is, scanf is pretty lousy for string
input.  It's better to use fgets(), and if that's
not quite what you want, it's a trivial job to
write your own getline() function.  Here's mine
(It doesn't do any error checking, but that's
easy to add.  I never write utility functions
with the assumption that I'm going to shoot myself
in the foot by passing a null pointer or something
like that, and I usually trust that getc() is not
going to fail):


#include <stdio.h>

/* int fgetline(FILE *fp, int size, char *buf)
   Reads characters from fp into buf, '\0'-terminates buf.
   stops when a newline is encountered or size-1
   characters have been read.  Does not store
   final newline.  size includes terminating '\0',
   so size must be at least 2.

   Returns: number of characters read (not counting \n), or -1 if
   at end of file before reading.
*/

int fgetline(FILE *fp,int size, char *buf)
{
    int n = 0,ch;

    if (feof(fp)) return -1;

    ch = getc(fp);
    while ((ch != EOF) && (ch != '\n') && (++n < size)) {
        *buf++ = (char) ch;
        ch = getc(fp);
    }

    *buf  = '\0'
    return n-1;
}
   


--Joe English

  jeenglis@nunki.usc.edu

xmjschm@hscfvax.harvard.edu (MJSchmelzer) (02/09/89)

Thanks to everyone who suggested using fgets() instead of gets().
(There, that was my summary of all the answers mailed to me.)

fgets() hadn't even occurred to me because I naively assumed it was like
fscanf() and fprint() in that it only differed by adding a stream
parameter.
   Fortunately, fgets() takes 3 parameters, including the all-important
buffer-size parameter. Here is the prototype (K&R2, p. 165):

char * fgets(char * s, int n, FILE * iop);

One final question: No one mentioned scanf("%255s", buffer).
This _does_ seem wrong to me, but I can't quite pin it down. Comments?

Thanks again!

-- 
==============  xmjschm@harvspha.BITNET ====== There are two kinds of people:
Mike Schmelzer  xmjschm@hscfvax.harvard.edu    They call each other "boring"
============================================== and "weird." -Me

guy@auspex.UUCP (Guy Harris) (02/09/89)

 >I mean, gets() in such a loop does just fine, ie it would return "foo
 >bar", but I have yet to kludge up a functionally equivalent scanf().
 >
 >Does this mean gets() is indispensable ? 

No, it means no such thing.  Look under "f", as in "fgets()" in your
reference manual.  Yes, it leaves a '\n' at the end of the line, but
it's not *that* big of a pain to remove it.

lvc@cbnews.ATT.COM (Lawrence V. Cipriani) (02/09/89)

In article <1989Feb8.223554.27192@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
>Printf() was an experiment that worked;
The printf we have today is not the same as the original (pre K&R) printf,
its first argument was a file descriptor.  I think the file descriptor was
dropped from printf when fprintf and FILEs were invented.
-- 
Larry Cipriani, att!cbnews!lvc or lvc@cbnews.att.com
        swtch(); /* no deposit, no return */

henry@utzoo.uucp (Henry Spencer) (02/10/89)

In article <3909@cbnews.ATT.COM> lvc@cbnews.ATT.COM (Lawrence V. Cipriani) writes:
>>Printf() was an experiment that worked;
>The printf we have today is not the same as the original (pre K&R) printf,
>its first argument was a file descriptor.  I think the file descriptor was
>dropped from printf when fprintf and FILEs were invented.

Uh, can you cite references for that?  Printf certainly did not take a file
descriptor as of Fifth Edition Unix, aka V5, released around the end of
1974 -- I remember V5.  I remember the arrival of fprintf and FILEs, and
the arrival of K&R; there was no change to printf on either occasion.
Well, actually, printf did pick up features in its format string at times,
but it hasn't taken a descriptor argument since I've known it.  This lack
was considered really annoying by a lot of people, and most everybody was
happy to see stdio arrive with fprintf.  (There was an earlier "portable"
library that may have included such a thing -- I don't remember it too
clearly -- but it had other problems and wasn't really suitable for
general use.)
-- 
The Earth is our mother;       |     Henry Spencer at U of Toronto Zoology
our nine months are up.        | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

karl@haddock.ima.isc.com (Karl Heuer) (02/14/89)

In article <722@hscfvax.harvard.edu> xmjschm@hscfvax.UUCP (R00100@MJSchmelzer) writes:
>One final question: No one mentioned scanf("%255s", buffer) [as a gets()
>replacement].  This _does_ seem wrong to me, but I can't quite pin it down.
>Comments?

I guess my mail didn't get through.  %s is wrong because it scans a token
rather than a line, but I did mention
	scanf("%254[^\n]%*c", buffer)
as a possible solution.  (I used 254 since you said the buffer was char[255],
and we need one byte for the trailing \0.)  This no longer crashes on long
lines, but it still fails to detect them.  (This could be fixed via
	char ch;
	if (scanf("%254[^\n]%c", buffer, &ch) != 2 || ch != '\n') error();
, but fgets() seems cleaner.)

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint