Custom Scanf Routine

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

    Custom Scanf Routine

    I have tried to write my custom scanf function on the lines of minprintf
    provided in K&R2.In the function myscanf() i access memory directly
    using the address passed to the function .Can it be dangerous ?

    I am getting the correct output though.Any help is appreciated.

    /*Include Files*/

    /*Assisting Functions*/
    int flushln(FILE *f){ /*Code*/}
    char *input(char *message){/*Code*/}
    static int getInt(void){/*Code*/}


    /*My Custom Scanf Routine*/

    int myscanf(const char* format,...)
    {
    va_list ap;
    const char *p;
    int count = 0;
    int temp;

    va_start(ap,for mat);
    for( p = format ; *p ;p++) {
    if( *p != '%'){
    continue;
    }
    switch(*++p){
    case 'd':
    if(temp = getInt()){
    *(long *)va_arg(ap,int ) = temp;/*Direct Memory Access*/
    count ++;
    }
    else{ puts("Input Error"); }
    break;
    }
    }
    va_end(ap);
    return count;
    }

    int main(void)
    {
    int a = 0,b = 0;
    myscanf("%d %d",&a,&b);
    printf("%d %d",a,b);

    return 0;
    }

    Thanks!
  • viza

    #2
    Re: Custom Scanf Routine

    Hi

    On Fri, 04 Jul 2008 03:12:09 +0530, Tarique wrote:
    I have tried to write my custom scanf function
    *(long *)va_arg(ap,int ) = temp;
    That is a very bad idea, it might work on certain systems where pointers
    are the same size as integers, but that is often not the case.

    Even if this does work once, the next operation on ap might fail.

    va_arg needs to be told the type of the argument that was written in
    place of the ... in when the function was called. You should always put
    the correct type in the second argument. In most cases that means you
    won't need to cast the return.

    * va_arg(ap,long* ) = temp;

    will work perfectly, as long as a long pointer was really written at the
    current place in the argument list.

    HTH
    viza

    Comment

    • Tarique

      #3
      Re: Custom Scanf Routine

      Tarique wrote:
      ....snip...

      This is my code for a minimal custom scanf function(for entering valid
      ints n longs with error checking)
      I would be really grateful if someone can review it.Comments awaited!

      Thank You

      #include <errno.h>
      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      #include <limits.h>
      #include <stdarg.h>

      #define BUFFSIZE 98 + 2 /*Used by input()*/

      /*Clear The Input stream if required*/
      int flushln(FILE *f) {
      int ch;
      while (('\n' != (ch = getc( f ))) && (EOF != ch))
      continue;
      return ch;
      }

      /*Accept a string from user (parse later on)*/
      char *input(const char* message,char *buff)
      {
      char buffer[ BUFFSIZE ];
      char *p = &buffer[ BUFFSIZE-1 ];

      if(message != "")
      puts(message);

      buffer[BUFFSIZE -1] = '$';

      if( fgets(buffer,BU FFSIZE,stdin) == NULL) {
      if(ferror(stdin )) {
      perror("Input Stream Error");
      clearerr( stdin );
      return "I/O";
      }
      if(feof(stdin)) {
      perror("EOF Encountered");
      clearerr( stdin );
      return "EOF";
      }
      }
      else{
      if(!((*p == '$') || ( (*p == '\0') && (*--p == '\n') ))) {
      flushln(stdin);
      puts("Too long input!\n\n");
      return NULL;
      }
      }
      strcpy(buff,buf fer);
      return buff;
      }

      /*Accept a string and pass a valid long int if possible,else return 0*/
      long getInt(void)
      {
      char intbuff[100];
      char *buffptr = intbuff;
      char *end_ptr;
      long int lVal;
      int trial = 1; /*No of tries on wrong input*/
      int dist=0;
      errno = 0;

      while(trial-- != 0)
      {
      buffptr = input("Enter :",intbuff);
      if (buffptr == "I/O" || buffptr == "EOF" || buffptr == NULL)
      return 0;

      lVal= strtol(buffptr, &end_ptr, 0);
      if (ERANGE == errno){
      perror("Out of Range");
      }
      else if (lVal INT_MAX){
      perror("Too Large!");
      }
      else if (lVal < INT_MIN){
      perror("Too Small");
      }
      else if (end_ptr == buffptr){
      printf("Not a Valid Integer\n\n");
      }
      else
      return lVal;
      }
      return 0;
      }

      int myscanf(const char* format,...)
      {
      va_list ap;
      const char *p;
      int count = 0;
      int temp;

      va_start(ap,for mat);
      for( p = format ; *p ;p++) {
      if( *p != '%'){
      continue;
      }
      switch(*++p){
      case 'd':
      if(temp =(int)getInt()) {
      *va_arg(ap,long *) = temp;/* Direct Memory Access to MEM */
      count ++;
      }
      else break;
      break;

      case 'l':
      if((*++p) == 'd'){
      if(temp =getInt()){
      *va_arg(ap,long *) = temp;/* Direct Memory Access to MEM */
      count ++;
      p--;
      }
      else {
      *va_arg(ap,long *) = 0;/*Invalid input ,so set variable to zero*/
      p--;
      break;
      }
      }
      break;
      default:
      break;
      }
      }
      va_end(ap);
      return count;
      }

      int main(void)
      {
      long int a = 0,b = 0;
      int c = 0,d = 0;

      myscanf("%ld %ld",&a,&b);
      printf("%ld %ld \n\n",a,b);

      myscanf("%d %d",&c,&d);
      printf("%d %d",c,d);

      return 0;
      }

      Comment

      • santosh

        #4
        Re: Custom Scanf Routine

        Tarique wrote:
        Tarique wrote:
        ...snip...
        >
        This is my code for a minimal custom scanf function(for entering valid
        ints n longs with error checking)
        I would be really grateful if someone can review it.Comments awaited!
        >
        Thank You
        Just a note. Haven't seen it in detail, sorry.

        Also in future please use spaces in place of tabs when posting to
        Usenet. As you can see below, some software on the Usenet stripped out
        all your tabs, rendering unformatted code, which is very difficult to
        read.
        #include <errno.h>
        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
        #include <limits.h>
        #include <stdarg.h>
        >
        #define BUFFSIZE 98 + 2 /*Used by input()*/
        >
        /*Clear The Input stream if required*/
        int flushln(FILE *f) {
        int ch;
        while (('\n' != (ch = getc( f ))) && (EOF != ch))
        continue;
        return ch;
        }
        >
        /*Accept a string from user (parse later on)*/
        char *input(const char* message,char *buff)
        {
        char buffer[ BUFFSIZE ];
        char *p = &buffer[ BUFFSIZE-1 ];
        >
        if(message != "")
        puts(message);
        >
        buffer[BUFFSIZE -1] = '$';
        >
        if( fgets(buffer,BU FFSIZE,stdin) == NULL) {
        if(ferror(stdin )) {
        perror("Input Stream Error");
        clearerr( stdin );
        return "I/O";
        }
        if(feof(stdin)) {
        perror("EOF Encountered");
        clearerr( stdin );
        return "EOF";
        }
        }
        else{
        if(!((*p == '$') || ( (*p == '\0') && (*--p == '\n') ))) {
        flushln(stdin);
        puts("Too long input!\n\n");
        return NULL;
        }
        }
        strcpy(buff,buf fer);
        return buff;
        }
        >
        /*Accept a string and pass a valid long int if possible,else return
        0*/ long getInt(void)
        {
        char intbuff[100];
        char *buffptr = intbuff;
        char *end_ptr;
        long int lVal;
        int trial = 1; /*No of tries on wrong input*/
        int dist=0;
        errno = 0;
        >
        while(trial-- != 0)
        {
        buffptr = input("Enter :",intbuff);
        if (buffptr == "I/O" || buffptr == "EOF" || buffptr == NULL)
        return 0;
        >
        lVal= strtol(buffptr, &end_ptr, 0);
        if (ERANGE == errno){
        perror("Out of Range");
        }
        else if (lVal INT_MAX){
        perror("Too Large!");
        }
        else if (lVal < INT_MIN){
        perror("Too Small");
        }
        What will you do on implementations where INT_MAX == LONG_MAX and
        INT_MIN == LONG_MIN?

        <snip rest>

        Comment

        • viza

          #5
          Re: Custom Scanf Routine

          On Fri, 04 Jul 2008 18:10:51 +0530, Tarique wrote:
          I would be really grateful if someone can review it.Comments awaited!
          #define BUFFSIZE 98 + 2 /*Used by input()*/
          Generally it's not smart to use arbitrary fixed length buffers. Are you
          certain that the input won't be bigger? If your code is well written
          then it that would cause it to fail, and if it is not well written then
          it could FEYC (f***ing explode your computer).
          /*Clear The Input stream if required*/ int flushln(FILE *f) {
          int ch;
          while (('\n' != (ch = getc( f ))) && (EOF != ch))
          continue;
          return ch;
          }
          Gah! ugly. It's best that you don't use assignments as truth values,
          especially as a beginner. That continue is superfluous too. I would
          also ungetc() the character and return void.
          /*Accept a string from user (parse later on)*/ char *input(const char*
          message,char *buff) {
          char buffer[ BUFFSIZE ];
          char *p = &buffer[ BUFFSIZE-1 ];
          char *p= buffer + BUFSIZE - 1; might be easier to read.
          if(message != "")
          You can't do that in C. Well, you can, but it doesn't mean what you
          think. Lookup the strcmp() function.
          puts(message);
          buffer[BUFFSIZE -1] = '$';
          if( fgets(buffer,BU FFSIZE,stdin) == NULL) {
          if(ferror(stdin )) {
          perror("Input Stream Error");
          clearerr( stdin );
          return "I/O";
          It's not usual to return strings like that one. In case of error perhaps
          you might want to return NULL.
          }
          if(feof(stdin)) {
          perror("EOF Encountered");
          clearerr( stdin );
          return "EOF";
          }
          }
          else{
          if(!((*p == '$') || ( (*p == '\0') && (*--p == '\n') )))
          {
          flushln(stdin);
          puts("Too long input!\n\n");
          return NULL;
          }
          }
          strcpy(buff,buf fer);
          If you know that buff is at least as big as buffer, why do you need
          buffer in the first place? Read to buff.
          return buff;
          }
          >
          /*Accept a string and pass a valid long int if possible,else return 0*/
          long getInt(void)
          {
          char intbuff[100];
          char *buffptr = intbuff;
          char *end_ptr;
          long int lVal;
          int trial = 1; /*No of tries on wrong input*/ int dist=0;
          errno = 0;
          >
          while(trial-- != 0)
          {
          buffptr = input("Enter :",intbuff);
          if (buffptr == "I/O" || buffptr == "EOF" || buffptr == NULL)
          return 0;
          >
          lVal= strtol(buffptr, &end_ptr, 0);
          if (ERANGE == errno){
          You didn't set errno to zero. Well you did, but it might have changed
          many times since then. Do it immediately before each conversion.

          I haven't gome through it all, but HTH
          viza

          Comment

          • Tarique

            #6
            Re: Custom Scanf Routine

            viza wrote:
            ...snip...
            >
            I haven't gome through it all, but HTH
            viza
            @Santosh and Viza

            Thanks for the quick review.Ive pasted the code here :
            Get the premium .com domain you deserve. Seamless and professional transactions. Payment plans available.

            Hope it is easier to read.

            Comment

            • Barry Schwarz

              #7
              Re: Custom Scanf Routine

              On Fri, 04 Jul 2008 18:10:51 +0530, Tarique <peo_leo@yahoo. comwrote:
              >This is my code for a minimal custom scanf function(for entering valid
              >ints n longs with error checking)
              >I would be really grateful if someone can review it.Comments awaited!
              >
              >Thank You
              >
              >#include <errno.h>
              >#include <stdio.h>
              >#include <stdlib.h>
              >#include <string.h>
              >#include <limits.h>
              >#include <stdarg.h>
              >
              >#define BUFFSIZE 98 + 2 /*Used by input()*/
              >
              >/*Clear The Input stream if required*/
              >int flushln(FILE *f) {
              int ch;
              while (('\n' != (ch = getc( f ))) && (EOF != ch))
              > continue;
              return ch;
              >}
              >
              >/*Accept a string from user (parse later on)*/
              >char *input(const char* message,char *buff)
              >{
              > char buffer[ BUFFSIZE ];
              > char *p = &buffer[ BUFFSIZE-1 ];
              >
              > if(message != "")
              puts(message);
              >
              > buffer[BUFFSIZE -1] = '$';
              >
              > if( fgets(buffer,BU FFSIZE,stdin) == NULL) {
              > if(ferror(stdin )) {
              > perror("Input Stream Error");
              > clearerr( stdin );
              > return "I/O";
              > }
              > if(feof(stdin)) {
              > perror("EOF Encountered");
              > clearerr( stdin );
              > return "EOF";
              > }
              > }
              > else{
              > if(!((*p == '$') || ( (*p == '\0') && (*--p == '\n') ))) {
              Please don't use tabs, especially within a line.

              This test is backwards. If *p is '$', then a short line was entered
              and there is nothing to flush. If the second half is true, exactly
              BUFFSIZE-1 characters were entered (including the Enter key) and there
              is still nothing to flush.

              An easier test would be to set buffer[BUFFSIZE-2] to '\n'. If it is a
              different non-zero value after the fgets, then too much data was
              entered.
              > flushln(stdin);
              > puts("Too long input!\n\n");
              > return NULL;
              > }
              > }
              > strcpy(buff,buf fer);
              > return buff;
              >}
              >
              >/*Accept a string and pass a valid long int if possible,else return 0*/
              >long getInt(void)
              >{
              > char intbuff[100];
              > char *buffptr = intbuff;
              > char *end_ptr;
              > long int lVal;
              > int trial = 1; /*No of tries on wrong input*/
              > int dist=0;
              > errno = 0;
              >
              > while(trial-- != 0)
              > {
              > buffptr = input("Enter :",intbuff);
              > if (buffptr == "I/O" || buffptr == "EOF" || buffptr == NULL)
              > return 0;
              >
              > lVal= strtol(buffptr, &end_ptr, 0);
              You need to indent consistently. This statement is not part of the
              range of the preceding if. It should be indented to the same level as
              that if.
              > if (ERANGE == errno){
              You should reset errno before calling strtol so that you don't process
              residual data. strtol is not allowed to reset errno so you must do
              it.
              > perror("Out of Range");
              > }
              > else if (lVal INT_MAX){
              > perror("Too Large!");
              > }
              > else if (lVal < INT_MIN){
              > perror("Too Small");
              > }
              > else if (end_ptr == buffptr){
              > printf("Not a Valid Integer\n\n");
              This is insufficient. Input of the form "123abc" will appear valid.
              To be sure that strtol processed all the characters, make sure end_ptr
              points to either '\n' or '\0'.
              > }
              > else
              > return lVal;
              How do you distinguish between input of 0 and one of the "error"
              conditions such as "I/O"?
              > }
              > return 0;
              >}
              >
              >int myscanf(const char* format,...)
              >{
              > va_list ap;
              > const char *p;
              > int count = 0;
              > int temp;
              >
              > va_start(ap,for mat);
              > for( p = format ; *p ;p++) {
              > if( *p != '%'){
              > continue;
              > }
              > switch(*++p){
              These two statements are within the range of the for but you indenting
              makes them look independent.
              > case 'd':
              > if(temp =(int)getInt()) {
              The cast is not needed.

              Why are you using %d to describe a long and confuse everyone.

              Why do you not accept 0 as valid input?
              > *va_arg(ap,long *) = temp;/* Direct Memory Access to MEM */
              > count ++;
              > }
              > else break;
              This is superfluous. If you remove it, the code will behave the same.
              > break;
              >
              > case 'l':
              > if((*++p) == 'd'){
              > if(temp =getInt()){
              > *va_arg(ap,long *) = temp;/* Direct Memory Access to MEM */
              > count ++;
              > p--;
              > }
              > else {
              > *va_arg(ap,long *) = 0;/*Invalid input ,so set variable to zero*/
              I still don't understand why 0 is illegal but it sure seems like a
              deliberate decision.
              > p--;
              > break;
              > }
              > }
              > break;
              > default:
              > break;
              > }
              If the value processed for %d is 0, you do not store any value for the
              corresponding argument AND you do not pop the argument off the list.

              If the value processed for %ld is 0, you store 0 and pop the argument
              but don't increment count.

              There could be an inconsistency between the value of count returned
              and which arguments contain valid data.
              > }
              > va_end(ap);
              > return count;
              >}
              >
              >int main(void)
              >{
              > long int a = 0,b = 0;
              > int c = 0,d = 0;
              >
              > myscanf("%ld %ld",&a,&b);
              > printf("%ld %ld \n\n",a,b);
              >
              > myscanf("%d %d",&c,&d);
              Here you invoke undefined behavior. myscanf will treat &c and &d as
              long int* when they aren't. I think you meant for myscanf to extract
              an int*, not a long*, for the %d case.
              > printf("%d %d",c,d);
              >
              > return 0;
              >}

              Remove del for email

              Comment

              • Barry Schwarz

                #8
                Re: Custom Scanf Routine

                On Fri, 04 Jul 2008 14:30:08 -0700, Barry Schwarz <schwarzb@dqel. com>
                wrote:
                >On Fri, 04 Jul 2008 18:10:51 +0530, Tarique <peo_leo@yahoo. comwrote:
                >
                >>This is my code for a minimal custom scanf function(for entering valid
                >>ints n longs with error checking)
                >>I would be really grateful if someone can review it.Comments awaited!
                >>
                >>Thank You
                >>
                >>#include <errno.h>
                >>#include <stdio.h>
                >>#include <stdlib.h>
                >>#include <string.h>
                >>#include <limits.h>
                >>#include <stdarg.h>
                >>
                >>#define BUFFSIZE 98 + 2 /*Used by input()*/
                >>
                >>/*Clear The Input stream if required*/
                >>int flushln(FILE *f) {
                > int ch;
                > while (('\n' != (ch = getc( f ))) && (EOF != ch))
                >> continue;
                > return ch;
                >>}
                >>
                >>/*Accept a string from user (parse later on)*/
                >>char *input(const char* message,char *buff)
                >>{
                >> char buffer[ BUFFSIZE ];
                >> char *p = &buffer[ BUFFSIZE-1 ];
                >>
                >> if(message != "")
                > puts(message);
                >>
                >> buffer[BUFFSIZE -1] = '$';
                >>
                >> if( fgets(buffer,BU FFSIZE,stdin) == NULL) {
                >> if(ferror(stdin )) {
                >> perror("Input Stream Error");
                >> clearerr( stdin );
                >> return "I/O";
                >> }
                >> if(feof(stdin)) {
                >> perror("EOF Encountered");
                >> clearerr( stdin );
                >> return "EOF";
                >> }
                >> }
                >> else{
                >> if(!((*p == '$') || ( (*p == '\0') && (*--p == '\n') ))) {
                >
                >Please don't use tabs, especially within a line.
                >
                >This test is backwards.
                Obviously I missed the !.


                Remove del for email

                Comment

                • CBFalconer

                  #9
                  Re: Custom Scanf Routine

                  viza wrote:
                  Tarique wrote:
                  >
                  .... snip ...
                  >
                  >/*Clear The Input stream if required*/ int flushln(FILE *f) {
                  > int ch;
                  > while (('\n' != (ch = getc( f ))) && (EOF != ch))
                  > continue;
                  > return ch;
                  >}
                  >
                  Gah! ugly. It's best that you don't use assignments as truth
                  values, especially as a beginner. That continue is superfluous
                  too. I would also ungetc() the character and return void.
                  Not in the least ugly. Apart from the awkward location of the
                  initial comment. Works like a charm, too. The continue prevents
                  misreading an isolated semi. Your ungetc recommendation would make
                  the routine fail to perform the function indicated by the name.

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


                  Comment

                  • viza

                    #10
                    Re: Custom Scanf Routine

                    On Fri, 04 Jul 2008 19:48:08 -0400, CBFalconer wrote:
                    viza wrote:
                    >Tarique wrote:
                    >>/*Clear The Input stream if required*/ int flushln(FILE *f) {
                    >> int ch;
                    >> while (('\n' != (ch = getc( f ))) && (EOF != ch))
                    >> continue;
                    >> return ch;
                    >>}
                    >>
                    >Gah! ugly. It's best that you don't use assignments as truth values,
                    >especially as a beginner. That continue is superfluous too. I would
                    >also ungetc() the character and return void.
                    >
                    Not in the least ugly.
                    I wouldn't take it home.
                    Apart from the awkward location of the initial comment. Works like a
                    charm, too. The continue prevents misreading an isolated semi.
                    Your ungetc recommendation would make the routine fail to perform the
                    function indicated by the name.
                    True. I misread the ugly loop construct and thought it read one-past the
                    newline.


                    Comment

                    • Tarique

                      #11
                      Re: Custom Scanf Routine

                      Barry Schwarz wrote:
                      On Fri, 04 Jul 2008 18:10:51 +0530, Tarique <peo_leo@yahoo. comwrote:
                      >
                      ....snip..
                      >int main(void)
                      >{
                      > long int a = 0,b = 0;
                      > int c = 0,d = 0;
                      >>
                      > myscanf("%ld %ld",&a,&b);
                      > printf("%ld %ld \n\n",a,b);
                      >>
                      > myscanf("%d %d",&c,&d);
                      >
                      Here you invoke undefined behavior. myscanf will treat &c and &d as
                      long int* when they aren't. I think you meant for myscanf to extract
                      an int*, not a long*, for the %d case.
                      Yes that is what i want it to do ! But why does that invoke UB ? I
                      thought the cast should produce correct result !!

                      Secondly Santosh had asked "What will you do on implementations where
                      INT_MAX == LONG_MAX and INT_MIN == LONG_MIN? "
                      On my system i have INT_MAX == LONG_MAX and INT_MIN == LONG_MIN
                      and the program seems to produce correct results.Am I missing something
                      there?

                      Well I,ve made the changes suggested and this the modified one . This
                      one produces results more consistently as expected(at least it seems to me!)

                      #include <errno.h>
                      #include <stdio.h>
                      #include <stdlib.h>
                      #include <string.h>
                      #include <limits.h>
                      #include <stdarg.h>

                      #define BUFFSIZE 98 + 2


                      int flushln(FILE *f) { /*Clear The Input stream if required*/
                      int ch;
                      while (('\n' != (ch = getc( f ))) && (EOF != ch))
                      ;
                      return ch;
                      }


                      /*Accept a string from user*/

                      char *input(const char* message,char *buff,size_t buff_len)
                      {
                      char buffer[ BUFFSIZE ];
                      char *p = buffer + BUFFSIZE - 1;

                      if(strcmp(messa ge,""))
                      puts(message);

                      buffer[BUFFSIZE -1] = '$';

                      if( fgets(buffer,BU FFSIZE,stdin) == NULL) {
                      if(ferror(stdin )) {
                      perror("Input Stream Error");
                      clearerr( stdin );
                      return "I/O";
                      }
                      else if(feof(stdin)) {
                      perror("EOF Encountered");
                      clearerr( stdin );
                      return "EOF";
                      }
                      }
                      else{
                      if(!((*p == '$') || ( (*p == '\0') && (*--p == '\n') ))) {
                      flushln(stdin);
                      puts("Too long input!\n\n");
                      return NULL;
                      }
                      }

                      if(buff_len >= strlen(buffer)) { /*Input longer than the dest buff can hold*/
                      strcpy(buff,buf fer);
                      return buff;
                      }
                      else
                      return NULL;
                      }

                      /*Accept a string and pass a valid long int if possible,else return 0*/
                      long getInt(void)
                      {
                      char intbuff[20];
                      char *buffptr = intbuff;
                      char *end_ptr = "\n";
                      long int lVal;
                      int trial = 1; /*No of trials allowed on error */
                      int dist=0;
                      errno = 0;

                      while(trial-- != 0)
                      {
                      buffptr = input("Enter :", intbuff, 19);

                      if(!strcmp(intb uff,"I/O")){
                      fprintf(stderr, "I/O Errro\n");
                      return -1;
                      }
                      else if (!strcmp(intbuf f,"EOF")){
                      fprintf(stderr, "EOF\n");
                      return -1;
                      }
                      else if(buffptr == NULL){
                      fprintf(stderr, "Too Long Input\n");
                      return -1;
                      }

                      errno = 0;
                      lVal= strtol(buffptr, &end_ptr, 0);


                      if (ERANGE == errno){
                      perror("Out of Range");
                      }
                      else if (lVal LONG_MAX){
                      perror("Too Large!");
                      }
                      else if (lVal < LONG_MIN){
                      perror("Too Small");
                      }
                      else if (end_ptr == buffptr){
                      printf("Not a Valid Integer\n\n");
                      }
                      else
                      return lVal;
                      }
                      return -1;
                      }

                      int myscanf(const char* format,...)
                      {
                      va_list ap;
                      const char *p;
                      int count = 0;
                      int temp = 0 ;

                      va_start(ap,for mat);
                      for( p = format ; *p ;p++) {
                      if( *p != '%'){
                      continue;
                      }
                      switch(*++p){
                      case 'd':
                      if(temp = (int)getInt()){
                      if(temp == 0)
                      *va_arg(ap,long *) = 0;/*Ugly Hack ?*/
                      *va_arg(ap,long *) = temp;/* Direct Memory Access*/
                      count ++;
                      }
                      else {
                      *va_arg(ap,long *) = 0;
                      p--;
                      break;
                      }

                      case 'l':
                      if((*++p) == 'd'){
                      if(temp = getInt()){
                      if(temp == 0)
                      *va_arg(ap,long *) = 0;/*Ugly Hack!*/
                      *va_arg(ap,long *) = temp;
                      count ++;
                      p--;
                      }
                      else {
                      *va_arg(ap,long *) = 0;
                      p--;
                      break;
                      }
                      }
                      default:
                      break;
                      }
                      }
                      va_end(ap);
                      return count;
                      }

                      int main(void)
                      {
                      long int a = 0,b = 0;
                      int c = 0,d = 0;

                      myscanf("%ld %ld",&a,&b);
                      printf("%ld %ld \n\n",a,b);

                      myscanf("%d %d",&c,&d);
                      printf("%d %d",c,d);

                      return 0;
                      }


                      Thanks for the suggestions.

                      Tarique

                      Comment

                      • Barry Schwarz

                        #12
                        Re: Custom Scanf Routine

                        On Sat, 05 Jul 2008 09:14:17 +0530, Tarique <peo_leo@yahoo. comwrote:
                        >Barry Schwarz wrote:
                        >On Fri, 04 Jul 2008 18:10:51 +0530, Tarique <peo_leo@yahoo. comwrote:
                        >>
                        >...snip..
                        >
                        >>int main(void)
                        >>{
                        >> long int a = 0,b = 0;
                        >> int c = 0,d = 0;
                        >>>
                        >> myscanf("%ld %ld",&a,&b);
                        >> printf("%ld %ld \n\n",a,b);
                        >>>
                        >> myscanf("%d %d",&c,&d);
                        >>
                        >Here you invoke undefined behavior. myscanf will treat &c and &d as
                        >long int* when they aren't. I think you meant for myscanf to extract
                        >an int*, not a long*, for the %d case.
                        >
                        >Yes that is what i want it to do ! But why does that invoke UB ? I
                        >thought the cast should produce correct result !!
                        There is no cast in the statement under discussion. &c is obviously
                        an int*. Your code in myscanf use the va_arg macro with an argument
                        of long*.

                        If the two types don't have the same size, va_arg will extract
                        the wrong number of bytes from the "argument list".
                        If an int* and long* are passed by different mechanisms (e.g.,
                        different registers), it may not even find the argument.
                        If the two pointer types don't have the same representation, the
                        address will be misinterpreted.
                        myscanf dereferences the argument on the left side of an
                        assignment. The address of the int could be not properly aligned for
                        a long.
                        If sizeof(long)>si zeof(int), you attempt to store too many bytes
                        in the int.

                        Each of these errors invokes undefined behavior.



                        Remove del for email

                        Comment

                        • Barry Schwarz

                          #13
                          Re: Custom Scanf Routine

                          On Sat, 05 Jul 2008 09:14:17 +0530, Tarique <peo_leo@yahoo. comwrote:

                          snip
                          >Well I,ve made the changes suggested and this the modified one . This
                          >one produces results more consistently as expected(at least it seems to me!)
                          Actually it is considerably worse.
                          >
                          >#include <errno.h>
                          >#include <stdio.h>
                          >#include <stdlib.h>
                          >#include <string.h>
                          >#include <limits.h>
                          >#include <stdarg.h>
                          >
                          >#define BUFFSIZE 98 + 2
                          >
                          >
                          >int flushln(FILE *f) { /*Clear The Input stream if required*/
                          >int ch;
                          >while (('\n' != (ch = getc( f ))) && (EOF != ch))
                          ;
                          return ch;
                          You are still not indenting with any consistency. The return is not
                          part of the while. You have the same problem in getInt.
                          >}
                          >
                          >
                          >/*Accept a string from user*/
                          >
                          >char *input(const char* message,char *buff,size_t buff_len)
                          >{
                          >char buffer[ BUFFSIZE ];
                          >char *p = buffer + BUFFSIZE - 1;
                          >
                          >if(strcmp(mess age,""))
                          > puts(message);
                          >
                          >buffer[BUFFSIZE -1] = '$';
                          >
                          >if( fgets(buffer,BU FFSIZE,stdin) == NULL) {
                          > if(ferror(stdin )) {
                          > perror("Input Stream Error");
                          > clearerr( stdin );
                          > return "I/O";
                          > }
                          > else if(feof(stdin)) {
                          > perror("EOF Encountered");
                          > clearerr( stdin );
                          > return "EOF";
                          > }
                          >}
                          >else{
                          > if(!((*p == '$') || ( (*p == '\0') && (*--p == '\n') ))) {
                          > flushln(stdin);
                          > puts("Too long input!\n\n");
                          > return NULL;
                          > }
                          >}
                          >
                          >if(buff_len >= strlen(buffer)) { /*Input longer than the dest buff can hold*/
                          > strcpy(buff,buf fer);
                          > return buff;
                          >}
                          >else
                          > return NULL;
                          >}
                          >
                          >/*Accept a string and pass a valid long int if possible,else return 0*/
                          >long getInt(void)
                          >{
                          >char intbuff[20];
                          >char *buffptr = intbuff;
                          >char *end_ptr = "\n";
                          >long int lVal;
                          >int trial = 1; /*No of trials allowed on error */
                          >int dist=0;
                          >errno = 0;
                          >
                          >while(trial-- != 0)
                          >{
                          > buffptr = input("Enter :", intbuff, 19);
                          >
                          > if(!strcmp(intb uff,"I/O")){
                          > fprintf(stderr, "I/O Errro\n");
                          > return -1;
                          > }
                          > else if (!strcmp(intbuf f,"EOF")){
                          > fprintf(stderr, "EOF\n");
                          > return -1;
                          > }
                          > else if(buffptr == NULL){
                          > fprintf(stderr, "Too Long Input\n");
                          > return -1;
                          > }
                          >
                          > errno = 0;
                          > lVal= strtol(buffptr, &end_ptr, 0);
                          If the user enters a number with a leading 0, it will be treated as
                          octal. Is that what you want?
                          >
                          >
                          > if (ERANGE == errno){
                          > perror("Out of Range");
                          > }
                          > else if (lVal LONG_MAX){
                          What makes you think strtol can ever return a value greater than
                          LONG_MAX?
                          > perror("Too Large!");
                          > }
                          > else if (lVal < LONG_MIN){
                          Or less than LONG_MIN?
                          > perror("Too Small");
                          > }
                          > else if (end_ptr == buffptr){
                          You still do not catch the error on input of "123abc".
                          > printf("Not a Valid Integer\n\n");
                          > }
                          > else
                          > return lVal;
                          >}
                          > return -1;
                          So you have decided that -1 will never be an acceptable input.
                          >}
                          >
                          >int myscanf(const char* format,...)
                          >{
                          >va_list ap;
                          >const char *p;
                          >int count = 0;
                          >int temp = 0 ;
                          >
                          >va_start(ap,fo rmat);
                          >for( p = format ; *p ;p++) {
                          > if( *p != '%'){
                          > continue;
                          >}
                          >switch(*++p) {
                          And here you have messed up the indenting even worse. It looks as if
                          the for loop terminates before the switch statement executes.
                          > case 'd':
                          > if(temp = (int)getInt()){
                          > if(temp == 0)
                          If you reach this statement, can it ever evaluate to true?
                          > *va_arg(ap,long *) = 0;/*Ugly Hack ?*/
                          Why do you think these two lines are needed? If temp is 0, what do
                          they do that the next line does not?

                          If temp is 0, you store 0 into two different objects, one in the above
                          statement and one in the following statement. Is that what you really
                          want to do?
                          > *va_arg(ap,long *) = temp;/* Direct Memory Access*/
                          The term Direct Memory Access has a special meaning in some hardware
                          designs. You are simply dereferencing a pointer.
                          > count ++;
                          There is no break here. You will fall into the 'l' case.
                          > }
                          > else {
                          > *va_arg(ap,long *) = 0;
                          > p--;
                          Why are you backing up to the % symbol?
                          > break;
                          > }
                          >
                          > case 'l':
                          > if((*++p) == 'd'){
                          > if(temp = getInt()){
                          Why do you care if the input value is 0?
                          > if(temp == 0)
                          > *va_arg(ap,long *) = 0;/*Ugly Hack!*/
                          > *va_arg(ap,long *) = temp;
                          > count ++;
                          > p--;
                          Why are you backing up to the l?
                          > }
                          > else {
                          > *va_arg(ap,long *) = 0;
                          > p--;
                          > break;
                          > }
                          > }
                          > default:
                          > break;
                          > }
                          >}
                          >va_end(ap);
                          >return count;
                          >}
                          >
                          >int main(void)
                          >{
                          >long int a = 0,b = 0;
                          >int c = 0,d = 0;
                          >
                          >myscanf("%ld %ld",&a,&b);
                          >printf("%ld %ld \n\n",a,b);
                          >
                          >myscanf("%d %d",&c,&d);
                          The undefined behavior is still here since myscan only processes long*
                          but you are passing int*.
                          >printf("%d %d",c,d);
                          >
                          >return 0;
                          >}

                          Remove del for email

                          Comment

                          • Tarique

                            #14
                            Re: Custom Scanf Routine

                            Barry Schwarz wrote:
                            On Sat, 05 Jul 2008 09:14:17 +0530, Tarique <peo_leo@yahoo. comwrote:
                            >
                            snip
                            >
                            >Well I,ve made the changes suggested and this the modified one . This
                            >one produces results more consistently as expected(at least it seems to me!)
                            >
                            Actually it is considerably worse.
                            Just could not believe that i had actually messed it up completely!
                            One more attempt ..phew!

                            I've re-typed the entire code and hope that the indentation doesn't get
                            messed up on usenet.This is the default indentation produced by MSVC++
                            08.I tried reducing it in the last post and the result was far worse!
                            If it still does, please read it here : http://phpfi.com/329403

                            Thanks again for all the patience :)


                            #include <stdio.h>
                            #include <errno.h>
                            #include <stdlib.h>
                            #include <string.h>
                            #include <limits.h>
                            #include <stdarg.h>

                            #define BUFFSIZE 98 + 2

                            int flushln(FILE *f)
                            {
                            int ch;
                            while(('\n' != (ch = getc( f ))) && (EOF != ch))
                            ;
                            return ch;
                            }

                            char* input(const char* message,char* buff,size_t buff_len)
                            {
                            char buffer[ BUFFSIZE ];
                            char* p = buffer + BUFFSIZE - 1;

                            if( strcmp( message, "" ) )
                            puts( message );

                            buffer[ BUFFSIZE - 1 ] = '$';

                            if( fgets( buffer, BUFFSIZE, stdin) == NULL ) {
                            if( ferror(stdin) ) {
                            perror( "Input Stream Error" );
                            clearerr( stdin );
                            return "I/O";
                            }
                            else if( feof(stdin) ) {
                            perror( "Input Stream Error" );
                            clearerr( stdin );
                            return "EOF";
                            }
                            }

                            else {
                            if(!((*p == '$') || ((*p == '\0') && (*--p == '\n')))) {
                            flushln( stdin );
                            return NULL ;
                            }
                            }

                            if( buff_len >= strlen(buffer) ) {
                            strcpy( buff, buffer );
                            return buff;
                            }
                            else {
                            return NULL;
                            }
                            }


                            long getInt( int* err_flag )
                            {
                            char intbuff[21] = {0} ;
                            char* buffptr = intbuff;
                            char* end_ptr = NULL;
                            long int lval = 0;
                            int trial = 1;
                            errno = 0;

                            while( trial-- )
                            {
                            buffptr = input( "Enter :", intbuff, 20 );

                            if( !strcmp( intbuff , "I/O" ) ) {
                            fprintf( stderr, "I/O Error \n" );
                            *err_flag = 0;
                            return -1;
                            }
                            else if( !strcmp( intbuff , "EOF" ) ) {
                            fprintf( stderr, "EOF \n" );
                            *err_flag = 0;
                            return -1;
                            }
                            else if( buffptr == NULL ) {
                            fprintf( stderr, "Too Long Input\n" );
                            *err_flag = 0;
                            return -1;
                            }

                            errno = 0;
                            lval = strtol( buffptr, &end_ptr, 10 );

                            if( ERANGE == errno ) {
                            *err_flag = 0;
                            perror( " Out of Range " );
                            }
                            else if ( lval INT_MAX ) {
                            *err_flag = 0;
                            perror( "Too Large " );
                            }
                            else if ( lval < INT_MIN ) {
                            *err_flag = 0;
                            perror( "Too Small" );
                            }
                            else if (!((*end_ptr == '\n') || (*end_ptr == '\0'))) {
                            *err_flag = 0;
                            fprintf( stderr, "Not a Valid Integer\n" );
                            }
                            else {
                            return lval;
                            }
                            }

                            *err_flag = 0;
                            return -1;
                            }


                            int myscanf( const char* format, ... )
                            {
                            va_list ap;
                            const char* p;
                            int count = 0;
                            int temp = 0;
                            int flag = 1;

                            va_start( ap, format );

                            for( p = format ; *p ; p++ ) {
                            if( *p != '%' ) {
                            continue;
                            }
                            switch( *++p ) {

                            case 'd' :
                            temp = (int)getInt( &flag );
                            if( flag ) {
                            *va_arg( ap, int* ) = temp;
                            count ++ ;
                            }
                            else {
                            *va_arg( ap, int* ) = 0;
                            flag = 1;
                            }

                            break;

                            case 'l':
                            if( (*++p) == 'd' ) {
                            temp = getInt( &flag );
                            if( flag ) {
                            *va_arg( ap, long * ) = temp;
                            count ++;
                            }
                            else {
                            *va_arg( ap, long * ) = 0;
                            flag = 1;
                            }
                            }

                            break;

                            default :
                            break;
                            }
                            }
                            va_end(ap);
                            return count;
                            }

                            int main(void)
                            {
                            long int a = 0, b = 0;
                            int c = 0, d = 0;

                            myscanf("%ld %ld",&a,&b);
                            printf("%ld %ld \n\n",a,b);

                            myscanf("%d %d",&c,&d);
                            printf("%d %d",c,d);

                            return 0;
                            }

                            Tarique

                            Comment

                            • Keith Thompson

                              #15
                              Re: Custom Scanf Routine

                              Tarique <peo_leo@yahoo. comwrites:
                              Barry Schwarz wrote:
                              >On Sat, 05 Jul 2008 09:14:17 +0530, Tarique <peo_leo@yahoo. comwrote:
                              >snip
                              >>
                              >>Well I,ve made the changes suggested and this the modified one
                              >>. This one produces results more consistently as expected(at least
                              >>it seems to me!)
                              >Actually it is considerably worse.
                              >
                              Just could not believe that i had actually messed it up completely!
                              One more attempt ..phew!
                              >
                              I've re-typed the entire code and hope that the indentation doesn't
                              get messed up on usenet.This is the default indentation produced by
                              MSVC++ 08.I tried reducing it in the last post and the result was far
                              worse!
                              If it still does, please read it here : http://phpfi.com/329403
                              >
                              Thanks again for all the patience :)
                              [...]

                              I haven't read the entire program, just bits and piece of it. At
                              least for now, all I have to offer is a few style points.

                              In flushln(), you use the (rather odd IMHO) idiom of "constant !=
                              expression":
                              while(('\n' != (ch = getc( f ))) && (EOF != ch))
                              In other places, you put the constant on the right hand side of the
                              comparison:
                              if(!((*p == '$') || ((*p == '\0') && (*--p == '\n')))) {
                              The usual reason to put the constant on the left is to avoid the error
                              of writing "=" rather than "==". Some programmers extend this to
                              operators other than "==". This:

                              if (x = 42) { ... }

                              is perfectly legal, but it assigns 42 to x rather than comparing x to
                              42. Some programmers cultivate the habit of writing:

                              if (42 == x) { ... }

                              so that, if they accidentally write "=" rather than "==", the
                              compiler will catch the error. You do this in some places, but not in
                              others.

                              My own opinion is that "if (42 == x)" is just ugly, and I never use it
                              myself; I find "if (x == 42") much more natural. But if you're going
                              to use it you should probably do so consistently. Or you can just be
                              careful not to write "=" when you mean "==".

                              Your function getInt() returns a result of type long. You call it
                              twice; both times, you assign the result to a variable of type int.
                              (In one you use an unnecessary cast; in the other you don't.) Why not
                              just declare getInt() to return an int? Or declare temp as a long?
                              Often (but by no means always), the need to convert something from one
                              type to another means that both things should have been of the same
                              type in the first place.

                              I stumbled over your invocations of va_arg, such as:
                              *va_arg( ap, int* ) = temp;
                              va_arg() is required to be a macro, and va_arg(ap, int*) expands to an
                              expression of type int*, so this seems to be valid.

                              But note that an invocation of va_arg *looks like* a function call.
                              If it really were, then the unary "*" operator would apply to the name
                              "va_arg", not to the result of the call, and the assignment would be
                              invalid.

                              So the assignment is valid, but it looks wrong. I'd definitely add
                              parentheses:

                              *(va_arg(ap, int*)) = temp;

                              --
                              Keith Thompson (The_Other_Keit h) kst-u@mib.org <http://www.ghoti.net/~kst>
                              Nokia
                              "We must do something. This is something. Therefore, we must do this."
                              -- Antony Jay and Jonathan Lynn, "Yes Minister"

                              Comment

                              Working...