fgets behaviour with strncmp

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Krumble Bunk

    #1

    fgets behaviour with strncmp

    Hi all,

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

    prompt <- \n
    prompt <- \n
    promptquit <- 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,MA XBUF,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
  • David Resnick

    #2
    Re: fgets behaviour with strncmp

    On Jun 17, 11:01 am, Krumble Bunk <krumbleb...@gm ail.comwrote:
    Hi all,
    >
    Having some trouble with a seemingly-simple task. Need to have a
    basic CLI functionality as per:
    >
    prompt <- \n
    prompt <- \n
    promptquit <- 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,MA XBUF,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

    Comment

    • Krumble Bunk

      #3
      Re: fgets behaviour with strncmp

      On Jun 17, 4:14 pm, David Resnick <lndresn...@gma il.comwrote:
      On Jun 17, 11:01 am, Krumble Bunk <krumbleb...@gm ail.comwrote:
      >
      >
      >
      Hi all,
      >
      Having some trouble with a seemingly-simple task. Need to have a
      basic CLI functionality as per:
      >
      prompt <- \n
      prompt <- \n
      promptquit <- 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,MA XBUF,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
      many thanks - fixed it nicely.

      thanks again

      kb.

      Comment

      • Flash Gordon

        #4
        Re: fgets behaviour with strncmp

        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,MA XBUF,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!
        }
        return 0;
        }
        --
        Flash Gordon

        Comment

        • santosh

          #5
          Re: fgets behaviour with strncmp

          Krumble Bunk wrote:
          Hi all,
          >
          Having some trouble with a seemingly-simple task. Need to have a
          basic CLI functionality as per:
          >
          prompt <- \n
          prompt <- \n
          promptquit <- should quit as per my snippet, but doesn't.
          <snip>

          #include <stdio.h>
          #include <stdlib.h>
          #include <string.h>
          #include <ctype.h>

          #define MAX_LINE 128

          int main(void)
          {
          char buf[MAX_LINE], *line = buf;
          int done = 0;

          do {
          fprintf(stdout, "");
          fflush(stdout);
          if (fgets(buf, sizeof buf/sizeof buf[0], stdin) == NULL) {
          fprintf(stderr, "Input error.\n");
          exit(EXIT_FAILU RE);
          }
          /* skip leading whitespace */
          while (isblank((int)* line)) line++;
          if (strcmp(line, "quit\n") == 0 || strcmp(line, "quit") == 0) {
          done = 1;
          }
          line = buf;
          } while (!done);
          return 0;
          }

          Comment

          • Ben Bacarisse

            #6
            Re: fgets behaviour with strncmp

            santosh <santosh.k83@gm ail.comwrites:
            Krumble Bunk wrote:
            >Having some trouble with a seemingly-simple task. Need to have a
            >basic CLI functionality as per:
            >>
            >prompt <- \n
            >prompt <- \n
            >promptquit <- should quit as per my snippet, but doesn't.
            >
            <snip>
            >
            #include <ctype.h>
            >
            int main(void)
            {
            <snip>
            while (isblank((int)* line)) line++;
            Presumably you intended to write isblank((unsign ed char)*line) here.
            I can't see any value in the cast to int.

            <snip>
            }
            --
            Ben.

            Comment

            • santosh

              #7
              Re: fgets behaviour with strncmp

              Ben Bacarisse wrote:
              santosh <santosh.k83@gm ail.comwrites:
              >
              >Krumble Bunk wrote:
              >>Having some trouble with a seemingly-simple task. Need to have a
              >>basic CLI functionality as per:
              >>>
              >>prompt <- \n
              >>prompt <- \n
              >>promptquit <- should quit as per my snippet, but doesn't.
              >>
              ><snip>
              >>
              >#include <ctype.h>
              >>
              >int main(void)
              >{
              <snip>
              > while (isblank((int)* line)) line++;
              >
              Presumably you intended to write isblank((unsign ed 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>

              Comment

              • Richard Heathfield

                #8
                Re: fgets behaviour with strncmp

                santosh said:

                <snip>
                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?
                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.

                --
                Richard Heathfield <http://www.cpax.org.uk >
                Email: -http://www. +rjh@
                Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
                "Usenet is a strange place" - dmr 29 July 1999

                Comment

                • santosh

                  #9
                  Re: fgets behaviour with strncmp

                  Richard Heathfield wrote:
                  santosh said:
                  >
                  <snip>
                  >
                  >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?
                  >
                  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.

                  Comment

                  • Richard Heathfield

                    #10
                    Re: fgets behaviour with strncmp

                    santosh said:
                    Richard Heathfield wrote:
                    >
                    >santosh said:
                    >>
                    ><snip>
                    >>
                    >>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?
                    >>
                    >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?
                    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.)

                    --
                    Richard Heathfield <http://www.cpax.org.uk >
                    Email: -http://www. +rjh@
                    Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
                    "Usenet is a strange place" - dmr 29 July 1999

                    Comment

                    • Barry Schwarz

                      #11
                      Re: fgets behaviour with strncmp

                      On Tue, 17 Jun 2008 08:01:59 -0700 (PDT), Krumble Bunk
                      <krumblebunk@gm ail.comwrote:
                      >Hi all,
                      >
                      >Having some trouble with a seemingly-simple task. Need to have a
                      >basic CLI functionality as per:
                      >
                      >prompt <- \n
                      >prompt <- \n
                      >promptquit <- 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,MA XBUF,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

                      Comment

                      • santosh

                        #12
                        Re: fgets behaviour with strncmp

                        Richard Heathfield wrote:
                        santosh said:
                        >
                        >Richard Heathfield wrote:
                        >>
                        >>santosh said:
                        >>>
                        >><snip>
                        >>>
                        >>>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?
                        >>>
                        >>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?
                        >
                        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.

                        Comment

                        • CBFalconer

                          #13
                          Re: fgets behaviour with strncmp

                          santosh wrote:
                          Ben Bacarisse wrote:
                          >
                          .... snip ...
                          >
                          >Presumably you intended to write isblank((unsign ed 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?
                          They're not. Below is the head from the standard. Note that the
                          type of the array is 'char', which may or may not be unsigned.

                          7.19.7.2 The fgets function
                          Synopsis
                          [#1]
                          #include <stdio.h>
                          char *fgets(char * restrict s, int n,
                          FILE * restrict stream);

                          --
                          [mail]: Chuck F (cbfalconer at maineline dot net)
                          [page]: <http://cbfalconer.home .att.net>
                          Try the download section.


                          ** Posted from http://www.teranews.com **

                          Comment

                          Working...