fgets behaviour with strncmp

K

Krumble Bunk

Hi all,

Having some trouble with a seemingly-simple task. Need to have a
basic CLI functionality as per:

prompt> <- \n
prompt> <- \n
prompt> quit <- should quit as per my snippet, but doesn't.

I've thrown a few random fflush()'s in, but no joy. I have never
really learnt the ANSI way ala fgets, fopen, etc.. so this is quite
new to me. Any advice on how I can actually make the program quit
when "quit" is entered would be appreciated :)

Code:

#include <stdio.h>

int main()
{

int done=0;
int len=0;
char buf[MAXBUF];

printf("> ");
while(!done)
{
if(fgets(buf,MAXBUF,stdin) < 0)
{
perror("fgets");
exit(1);
}

len=strlen(buf);
buf[len]='\0';

fflush(stdin);
if(*buf == '\n')
printf("> ");
printf("debug: %s\n",buf);
fflush(stdin);
fflush(stdout);
if(!strncmp(buf,"quit",len))
done=1;
}
return 0;
}

thanks

kb
 
D

David Resnick

Hi all,

Having some trouble with a seemingly-simple task. Need to have a
basic CLI functionality as per:

prompt> <- \n
prompt> <- \n
prompt> quit <- should quit as per my snippet, but doesn't.

I've thrown a few random fflush()'s in, but no joy. I have never
really learnt the ANSI way ala fgets, fopen, etc.. so this is quite
new to me. Any advice on how I can actually make the program quit
when "quit" is entered would be appreciated :)

Code:

#include <stdio.h>

int main()
{

int done=0;
int len=0;
char buf[MAXBUF];

printf("> ");
while(!done)
{
if(fgets(buf,MAXBUF,stdin) < 0)
{
perror("fgets");
exit(1);
}

len=strlen(buf);
buf[len]='\0';

fflush(stdin);
if(*buf == '\n')
printf("> ");
printf("debug: %s\n",buf);
fflush(stdin);
fflush(stdout);
if(!strncmp(buf,"quit",len))
done=1;
}
return 0;

}

thanks

kb

Lots of issues there, but your central problem is that buf includes
the newline. If you set (after appropriate checking) buf[len-1] to
'\0' and use strcmp you will fix that. A few other points:

don't fflush(stdin)
do include string.h, stdlib.h
do define MAXBUF somewhere
do turn on your compilers warnings...

-David
 
K

Krumble Bunk

Having some trouble with a seemingly-simple task. Need to have a
basic CLI functionality as per:
prompt> <- \n
prompt> <- \n
prompt> quit <- should quit as per my snippet, but doesn't.
I've thrown a few random fflush()'s in, but no joy. I have never
really learnt the ANSI way ala fgets, fopen, etc.. so this is quite
new to me. Any advice on how I can actually make the program quit
when "quit" is entered would be appreciated :)

#include <stdio.h>
int main()
{
int done=0;
int len=0;
char buf[MAXBUF];
printf("> ");
while(!done)
{
if(fgets(buf,MAXBUF,stdin) < 0)
{
perror("fgets");
exit(1);
}
len=strlen(buf);
buf[len]='\0';

fflush(stdin);
if(*buf == '\n')
printf("> ");
printf("debug: %s\n",buf);
fflush(stdin);
fflush(stdout);
if(!strncmp(buf,"quit",len))
done=1;
}
return 0;

kb

Lots of issues there, but your central problem is that buf includes
the newline. If you set (after appropriate checking) buf[len-1] to
'\0' and use strcmp you will fix that. A few other points:

don't fflush(stdin)
do include string.h, stdlib.h
do define MAXBUF somewhere
do turn on your compilers warnings...

-David

many thanks - fixed it nicely.

thanks again

kb.
 
F

Flash Gordon

Krumble Bunk wrote, On 17/06/08 16:01:

<snip>

In addition to the other comment you have seen...

Random changes to code (you mentioned throwing in random calls to
fflush) is NEVER a good way to develop.
#include <stdio.h>

int main()

Better would be
int main(void)
{

int done=0;
int len=0;
char buf[MAXBUF];

printf("> ");

In this instance a do-while loop makes more sense since you always want
to run the loop at least once.
while(!done)
{

fflush(stdout);

With your code the first prompt might not be displayed before waiting
for input.
if(fgets(buf,MAXBUF,stdin) < 0)
{
perror("fgets");
exit(1);

This is not a portable value to pass to exit. Only 0, EXIT_SUCCESS and
EXIT_FAILURE are portable. Some times there are good reasons to use
non-portable return codes, but not here.

Also, later you use a return rather than calling exit. You should be
consistent in which method you use to leave main. Personally I normally
use return.
}

len=strlen(buf);
buf[len]='\0';

This would not do what you want if too long a line was input. Check to
see if it was a newline first.
fflush(stdin);

fflush is not defined for input streams by the C standard. Some
implementations *might* define it, but others definitely don't.
if(*buf == '\n')
printf("> ");

Better in my opinion to be consistent about whether you are using array
or pointer operators on buf. Mind you, printing the prompt here seems
odd and doing so conditionally seems even more odd.
printf("debug: %s\n",buf);
fflush(stdin);
fflush(stdout);
if(!strncmp(buf,"quit",len))
done=1;

After switching to a do-while loop you could put the above test as the
condition and scrap the done flag!
 
S

santosh

Ben said:
Presumably you intended to write isblank((unsigned char)*line) here.
I can't see any value in the cast to int.

You are right. In fact, would the cast to unsigned char be necessary at
all, given that all the characters returned by fgets are guaranteed to
be positive integers?

<snip>
 
S

santosh

Richard said:
santosh said:



Chapter and verse, please. I think you're mistaken about the existence
of such a guarantee. It applies to fgetc, yes, but not to fgets.

Aren't all the standard byte input functions implemented as if they were
composed of successive calls to fgetc? If so, a guarantee for fgetc
should be applicable to functions modelled upon it too.
 
B

Barry Schwarz

Hi all,

Having some trouble with a seemingly-simple task. Need to have a
basic CLI functionality as per:

prompt> <- \n
prompt> <- \n
prompt> quit <- should quit as per my snippet, but doesn't.

I've thrown a few random fflush()'s in, but no joy. I have never
really learnt the ANSI way ala fgets, fopen, etc.. so this is quite
new to me. Any advice on how I can actually make the program quit
when "quit" is entered would be appreciated :)

Code:

#include <stdio.h>

int main()
{

int done=0;
int len=0;
char buf[MAXBUF];

printf("> ");
while(!done)
{
if(fgets(buf,MAXBUF,stdin) < 0)

fgets returns a char*, not an int. It will return NULL on end of file
or error. NULL is guaranteed to compare equal to 0 so your error
message will never get displayed. I doubt if a non-NULL return will
ever result in your if evaluating true.
{
perror("fgets");
exit(1);

Use EXIT_FAILURE for portability.
}

len=strlen(buf);
buf[len]='\0';

If your intent is to remove the '\n' that fgets will include in the
string if it can, then you need len-1.
fflush(stdin);

fflush is not defined for input streams. This invokes undefined
behavior.
if(*buf == '\n')

The only way this can evaluate to true is if the user hits enter
without hitting any data keys. If stdin has been redirected to a
file, then you would need an empty (not blank) line.
printf("> ");
printf("debug: %s\n",buf);
fflush(stdin);
fflush(stdout);
if(!strncmp(buf,"quit",len))
done=1;
}
return 0;
}


Remove del for email
 
S

santosh

Richard said:
santosh said:
Aren't all the standard byte input functions implemented as if they
were composed of successive calls to fgetc?
Yes.

If so, a guarantee for fgetc
should be applicable to functions modelled upon it too.

And so it is - for just as long as you are storing the results in an
array of unsigned char. But you're not, are you? You're storing them
(quite properly) in an array of char. So, on systems where char is
signed by default (and there are still quite a few of those about),
you have a problem if you assume that they are representable as
unsigned char without a conversion taking place. For example, consider
an input to fgets that includes a character with code point 130, on a
system with 8-bit char, signed by default. The fgets function will
read this as if via fgetc, so the value 130 will be returned from
fgetc (or "as-if-fgetc" if you prefer) as an int. This fulfils the
terms of the contract that you mentioned. Now fgets has to store this
value somewhere in your array of char. Since, on the system I've
mentioned, CHAR_MAX is 127, fgets will have to store the 130 into that
array as a negative number: -126 is a definite possibility (I know
because it's what happens on my system!), and -126 is not
representable as unsigned char, because unsigned char can't represent
negative values unless they are first converted into non-negative
values.

In the case in point, the C99 function isblank is being used. This is
one of the "is*" functions, prototyped in <ctype.h>, that require
their input to be either EOF or a value representable as an unsigned
char. We have discussed many times before how negative values can foul
this up. For example:

#define isblank(x) ( ((x) == EOF) ? (x) : __isblank[(x)] )

which invokes undefined behaviour if passed a negative value other
than EOF. (E&OE - my track record on illustrative is* macros is
appalling.)

Okay, many thanks for making that clear.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,528
Members
45,000
Latest member
MurrayKeync

Latest Threads

Top