LinuxQuestions.org
Share your knowledge at the LQ Wiki.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 02-24-2005, 08:24 AM   #1
speel
Member
 
Registered: Apr 2004
Posts: 354

Rep: Reputation: 30
simple C program problem


ok well my problem is scanf seems not to like spaces.. where it asks for your favorite food it craps out when you put somthing like rice and beans but if you put somthing just like carrots it handles it fine .. any suggestions? and and i forgot to mention im just learning .. like it matters



Code:
#include <stdio.h>
int
  main()
{
   
  int age; // sets age as an integer
  age = 16; // age can be changed to anything you want
  char name[8] = "anthony"; // name can be changed to what ever you want
  char food[20];// no need to touch
  char color[20];// no need to touch
  int born; // sets born as an integer

   printf("hi my name is %s and my age is %d\n",name, age);
   printf("o wait how about some more info?\n");
   
   printf("Whats your favorite food?\n");
     scanf("%s",food);
   printf("Whats your favorite color?\n");
     scanf("%s",color);
   printf("What year where you born?\n");
      scanf("%d",&born);
   
   printf("\n\nOk lets get this straight, your name is %s",name);
   printf("you are currently %d years old\n",age);
   printf("your favorite food is %s\n",food);
   printf("your favorite corlor is %s\n",color);
   printf("and last but not least you where born in the year %d\n",born);
   
   return 0;
}
 
Old 02-24-2005, 09:22 AM   #2
itsme86
Senior Member
 
Registered: Jan 2004
Location: Oregon, USA
Distribution: Slackware
Posts: 1,246

Rep: Reputation: 59
fgets() will work better for you. Something like:
Code:
fgets(food, sizeof(food), stdin);
if(food[strlen(food)-1] == '\n')
  food[strlen(food)-1] = '\0';
fgets() adds the \n (from when the user presses ENTER) to the buffer you pass to it (food in this case) so you have to chop it off with something like that third line of code.
 
Old 02-24-2005, 09:23 AM   #3
Hko
Senior Member
 
Registered: Aug 2002
Location: Groningen, The Netherlands
Distribution: Debian
Posts: 2,536

Rep: Reputation: 111Reputation: 111
Scanf() interprets spaces as seperators, and tries to write the text after a space to the next variable. Since you don't provide a next variable, scanf() start writing wildly into memory that belongs to other variables or even even beyond that, possibly resulting in a "segmentation fault" crash of your program.

With scanf, this can not really be solved, because you never know for sure how many words (texts seperated by one or more spaces) the user will try to type.

Use fgets() instead to read user input.
 
Old 02-26-2005, 11:05 PM   #4
skoona
Member
 
Registered: Mar 2004
Location: Indiana, USA
Distribution: Fedora, CentOS, Ubuntu, OS/X, Raspbian
Posts: 90

Rep: Reputation: 18
Code:
scanf("%[^\n]",food);
"%[^\n]" takes everything up to a newline char and assigns it to 'food' which must be a char *. I often use this to take from this point to the 'end of the line'.

Wheter its used in scanf to get string from cmdline, or in sscanf to parse a string returned from fgets; it takes everything including spaces EXPECT the newline. So you won't need to cleanup the string later with strlen.
 
Old 02-27-2005, 01:37 AM   #5
itsme86
Senior Member
 
Registered: Jan 2004
Location: Oregon, USA
Distribution: Slackware
Posts: 1,246

Rep: Reputation: 59
You might not need to go through the trouble of removing the \n from the end of the string, but you're introducing a gigantic security hole. scanf() has no way of knowing how big food is, so it will just copy everything the user types into it, even if it goes past the end of the array.

You might not care for little class projects or whatever, but it's better to start using good habits early.
 
Old 02-27-2005, 07:07 AM   #6
skoona
Member
 
Registered: Mar 2004
Location: Indiana, USA
Distribution: Fedora, CentOS, Ubuntu, OS/X, Raspbian
Posts: 90

Rep: Reputation: 18
Quote:
Originally posted by itsme86

You might not care for little class projects or whatever, but it's better to start using good habits early.
I do care, and you are right about developing good habits now. However, knowing about the buffer overrun problems associated with scanf use allows you to use it safely and in the correct fashion. You had already said it, so I didn't say it again...

Code:
char ch_food[521];
char ch_buffer[512];
...
fgets ( ch_buffer, sizeof(ch_buffer), fp );
...
sscanf (ch_buffer, "%[^\n]", ch_food );
...
The above example I think would resolve any concerns about buffer overruns, etc. From a logic standpoint, you may want to consider a different combination of statements - but thats what programming is all about...
 
Old 02-27-2005, 10:12 AM   #7
exvor
Senior Member
 
Registered: Jul 2004
Location: Phoenix, Arizona
Distribution: Gentoo, LFS, Debian,Ubuntu
Posts: 1,537

Rep: Reputation: 87
Hmm confuseing code here


Code:
fgets(food, sizeof(food), stdin);
if(food[strlen(food)-1] == '\n')     
  food[strlen(food)-1] = '\0';    <--Why do you have to terminate the string i thought fgets does this for you 

Code:
 
char ch_food[521];
char ch_buffer[512];
...
fgets ( ch_buffer, sizeof(ch_buffer), fp );
...
sscanf (ch_buffer, "%[^\n]", ch_food );    <--  This would be redudent  

...
 
Old 02-27-2005, 10:46 AM   #8
perfect_circle
Senior Member
 
Registered: Oct 2004
Location: Athens, Greece
Distribution: Slackware, arch
Posts: 1,783

Rep: Reputation: 53
Quote:
Originally posted by exvor
Hmm confuseing code here


Code:
fgets(food, sizeof(food), stdin);
if(food[strlen(food)-1] == '\n')     
  food[strlen(food)-1] = '\0';    <--Why do you have to terminate the string i thought fgets does this for you 
from fgets man page:
fgets() reads in at most one less than size characters from stream and
stores them into the buffer pointed to by s. Reading stops after an EOF
or a newline. If a newline is read, it is stored into the buffer. A
'\0' is stored after the last character in the buffer.
SO the above code is correct
Also another problem that may occur:
if you prompt for food, and the user types more charecters than the buffer size and then hits the enter:
lets say the buffer can store 6 chars: 5 chars and '\0'. If you type 8 chars and then '\n' (new line), the next time you do fgets (lets say for color) the function will get as input the remaining 3chars and the new line. So If you use this approach , you need to check if the buffer is filled, without getting a new line, and if this is the case, u nead to manually get the remaining chars till the new line(e.x. getchar() with a for loop) and ignore them, so you don't get them the next time you prompt for something else

Last edited by perfect_circle; 02-27-2005 at 10:52 AM.
 
Old 02-27-2005, 10:51 AM   #9
itsme86
Senior Member
 
Registered: Jan 2004
Location: Oregon, USA
Distribution: Slackware
Posts: 1,246

Rep: Reputation: 59
Quote:
Why do you have to terminate the string i thought fgets does this for you
I was just getting rid of the \n at the end.
Quote:
sscanf (ch_buffer, "%[^\n]", ch_food ); <-- This would be redudent
skoona is doing the same thing.

@skoona: Your method will work as well. I was specifically talking about scanf() in my statement, but you used sscanf(). The difference is that scanf() reads from an untrusted source (the user), and sscanf() works from a trusted source (a buffer that you've created).
 
Old 02-27-2005, 10:55 AM   #10
itsme86
Senior Member
 
Registered: Jan 2004
Location: Oregon, USA
Distribution: Slackware
Posts: 1,246

Rep: Reputation: 59
@perfect_circle: No, the correct code is the one I originally posted. You only want to subtract 1 from strlen(). If the user enters "hello" then fgets() will store this in the buffer: 'h' 'e' 'l' 'l' 'o' '\n' '\0' (assuming the buffer is sufficiently big).

strlen("hello\n") would return 6 and the \n is at index 5, so you only want to subtract 1.
 
Old 02-27-2005, 11:59 AM   #11
exvor
Senior Member
 
Registered: Jul 2004
Location: Phoenix, Arizona
Distribution: Gentoo, LFS, Debian,Ubuntu
Posts: 1,537

Rep: Reputation: 87
Sorry for my ignorance but talking about gets in another topic here im wondering then why

1. fgets is better then gets if it suffers the same issues with buffer overrun

2. Why is this error checking for over run not done alredy in the fgets function. <-- explianing this more would be like why then if you pass more from the source string does it not detect this and return a error value. basily all it would need to do is compare the 2 strings and if the source is larger then the destination not preform the action and return an error and dump the source.

Maybe this isnt possible but it kinda would make better sense to do this. Hence why Getline was created albeit it still has some issues one big one being portablity.
 
Old 02-27-2005, 12:59 PM   #12
perfect_circle
Senior Member
 
Registered: Oct 2004
Location: Athens, Greece
Distribution: Slackware, arch
Posts: 1,783

Rep: Reputation: 53
Quote:
Originally posted by itsme86
@perfect_circle: No, the correct code is the one I originally posted. You only want to subtract 1 from strlen(). If the user enters "hello" then fgets() will store this in the buffer: 'h' 'e' 'l' 'l' 'o' '\n' '\0' (assuming the buffer is sufficiently big).

strlen("hello\n") would return 6 and the \n is at index 5, so you only want to subtract 1.
I know, if you see the last edited time, my post is corrected before you posted this message.
Quote:
1. fgets is better then gets if it suffers the same issues with buffer overrun

2. Why is this error checking for over run not done alredy in the fgets function. <-- explianing this more would be like why then if you pass more from the source string does it not detect this and return a error value. basily all it would need to do is compare the 2 strings and if the source is larger then the destination not preform the action and return an error and dump the source.
1. Who said that fgets suffers from this issue?
2. fgets will get at most as many chars as the second field of the function(minus 1) implies. if the input stream contain more chars until the newline, a second fgets will get them. This is not an error. By simply checking the destination buffer you can determine what happend. If fgets worked the way you say, this would destroy fgets flexibility. fgets does not cause a buffer overrun at all if you are carefull, no matter what the input is.

Last edited by perfect_circle; 02-27-2005 at 01:02 PM.
 
Old 02-27-2005, 01:51 PM   #13
exvor
Senior Member
 
Registered: Jul 2004
Location: Phoenix, Arizona
Distribution: Gentoo, LFS, Debian,Ubuntu
Posts: 1,537

Rep: Reputation: 87
Sorry In testing i see what you mean by not a buffer over run it just doesent get the rest of the data from the input thats all.


yea and a simple for loop with getchar can fix the issue with extra data at the end if the user types more then the buffer.



Again im new to programming in c: so im still trying to get my way of dealing with these little issues.
 
Old 02-27-2005, 02:48 PM   #14
itsme86
Senior Member
 
Registered: Jan 2004
Location: Oregon, USA
Distribution: Slackware
Posts: 1,246

Rep: Reputation: 59
Here's a getline() function I made that will always get all the data the user typed no matter how big the buffer is. You can also use it for reading a line from a file:
Code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char *getline(char *buf, int bufsize, FILE *fp)
{
  char *line = NULL;
  int buflen, linelen = 0;

  while(fgets(buf, bufsize, fp))
  {
    buflen = strlen(buf);
    line = realloc(line, linelen+buflen+1);
    strcpy(line+linelen, buf);
    linelen += buflen;

    if(line[linelen-1] == '\n')
    {
      line[linelen-1] = '\0';
      break;
    }
  }

  return line;
}

int main(void)
{
  char buf[10];
  char *line;

  line = getline(buf, sizeof(buf), stdin);
  printf("You typed: %s\n", line);

  free(line);
  return 0;
}
Quote:
itsme@dreams:~/C$ ./getline
This line is much longer than the buffer which can only hold 10 characters. As you can see, it grabs all of it.
You typed: This line is much longer than the buffer which can only hold 10 characters. As you can see, it grabs all of it.
itsme@dreams:~/C$
 
Old 02-27-2005, 05:08 PM   #15
skoona
Member
 
Registered: Mar 2004
Location: Indiana, USA
Distribution: Fedora, CentOS, Ubuntu, OS/X, Raspbian
Posts: 90

Rep: Reputation: 18
This simple C program problem has ...

All these counter points serve to illustrate problems that can and do occur when taking keyboard, or otherwise untrusted, input using typical C programming statements. GUI's and ncurses were designed to bring this user interface back into a more trusted arena.

The point we, or at least I, want to make is buffer-overruns are reality and you must consider beforehand their impact. i.e. the ultimate user will not be happy with a program that does not work.

If I cannot use ncurses or a GUI to limit the users expectation of how much to input, then I use overly sized buffers in pairs. One BIG buffer(ch_buffer) (say 512 or 1024 or even 2048 ) as the main i/o buffer to get stuff - this ensure I get it all -, then I use sscanf (or something like it) to pickout the pieces I want and place it into a regular sized buffer(ch_food). Of course I always reuse the BIG buffer for the next input, and so on.

Wasting a few hundred bytes is fine in today's megabyte+ systems, if it protects the logic of your program and the users experience. Anytime you need to do IO against untrusted interfaces (be it web or keyboard) you should consider taking extra measures to protect against overruns. I often resort to strncpy vs strcpy, snprintf vs sprintf, and ctime_r vs ctime, strtok_r vs strtok, as a few examples of what the C library makes available to give your the extra control needed.

I hope this is helping you!
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
SDL problem... simple program. RHLinuxGUY Programming 1 07-22-2005 06:04 PM
simple program help! rohitkara Linux - General 1 05-24-2005 08:57 PM
simple c program liguorir Linux - Software 4 05-29-2004 06:22 AM
simple C program problem mined Programming 2 05-08-2004 05:42 AM
simple program big problem antken Programming 3 09-15-2002 10:39 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 01:52 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration