lianergoist
Posts: 33
Joined: Sat Nov 08, 2014 12:38 pm
Location: Horsens, Denmark

How do I debug stdin?

Fri Aug 10, 2018 4:14 pm

I hope this kind of question is on-topic here. Else, plese let me know.

I have a problem with the LF character. I have made a little demo to show the problem:

Code: Select all

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

void search_word() {
	
	char *tmp_string;
	size_t len=50;
	
	tmp_string = (char *) malloc(len * sizeof(char)+1);

	printf("\nEnter a word: ");
	fflush(stdout);
	fflush(stdin);

	getline(&tmp_string, &len, stdin);
	
	printf("%s\n", tmp_string);

	free(tmp_string);
}

int main() {
	
	int selected;
		
	while (1) {
		printf("Search another word? (y/n) ");
		fflush(stdout);
		
		do {
			selected = fgetc(stdin);
		} while (selected == '\n');
	
		if (selected == 'y') {
			fflush(stdin);
			search_word();
		}
		else if (selected == 'n') {
			break;
		}
		else {
			printf("Hit 'y' or 'n'\n");
		}
	}
	return 0;
}

When I run the program, it never let me enter a word. I get the following output:

Code: Select all

[email protected]:~/c/ooo $ gcc -o test8 -g test8.c
[email protected]:~/c/ooo $ ./test8
Search another word? (y/n) y

Enter a word: 

Search another word? (y/n) y

Enter a word: 

Search another word? (y/n) n
[email protected]:~/c/ooo $ 

It *looks* like the LF char is not flushed from stdin and therefore is send to getline().

I have two problems:

1. I don really understand this piece of code:

Code: Select all

do {
	selected = fgetc(stdin);
} while (selected == '\n');
It is something I found in a book and it should somehow make make the loop ignore the LF, but I don't understand why/how it does it - and it looks like it's not really working...? :-/ Can someone please explain why/how it works? Or maybe just suggest alternative ways to ignore the LF?

2. I have tried to run the code in gdb, but I don't know how I can view/print stdin. How can I see if stdin really is flushed and no LF is waiting?

Thanks...
Thomas Jensen

There are two types of people.
1) Those who can extrapolate from incomplete data

jahboater
Posts: 2913
Joined: Wed Feb 04, 2015 6:38 pm

Re: How do I debug stdin?

Fri Aug 10, 2018 4:49 pm

I suspect mixing fgetc and getline is confusing things.
Does this do what you want ?
I simplified it a little (there is no need to keep mallocing/freeing the buffer every time).

Code: Select all

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

static char *tmp_string = NULL;
static size_t tmp_len = 50;

void search_word() {
  printf("\nEnter a word: ");

  if( getline( &tmp_string, &tmp_len, stdin ) != -1 )
    printf("%s\n", tmp_string);
}

int main() {

  for(;;) {
    printf("Search another word? (y/n) ");
    if( getline( &tmp_string, &tmp_len, stdin ) != -1 )
    {
      const char selected = tmp_string[0];

      if( selected == 'y' )
        search_word();
      else if( selected == 'n' )
        break;
      else
        printf("Hit 'y' or 'n'\n");
    }
  }

}
Perhaps take a look at GNU readline().
It is very easy to use and allows command line editing amongst other things.

User avatar
rpdom
Posts: 12804
Joined: Sun May 06, 2012 5:17 am
Location: Ankh-Morpork

Re: How do I debug stdin?

Fri Aug 10, 2018 6:47 pm

lianergoist wrote:
Fri Aug 10, 2018 4:14 pm
1. I don really understand this piece of code:

Code: Select all

do {
	selected = fgetc(stdin);
} while (selected == '\n');
It is something I found in a book and it should somehow make make the loop ignore the LF, but I don't understand why/how it does it - and it looks like it's not really working...? :-/ Can someone please explain why/how it works? Or maybe just suggest alternative ways to ignore the LF?
It's a simple do-while loop. It loops until the condition in the while() is not true.

In psuedo-code:

Code: Select all

loop_start:
    read character from stdin
if character == "\n" go to loop_start
"\n" is the escape sequence that matches the LF character (0x0a ascii)

lianergoist
Posts: 33
Joined: Sat Nov 08, 2014 12:38 pm
Location: Horsens, Denmark

Re: How do I debug stdin?

Fri Aug 10, 2018 7:25 pm

jahboater wrote:
Fri Aug 10, 2018 4:49 pm
I suspect mixing fgetc and getline is confusing things.
Does this do what you want ?
Thank you for helping me. Yes, it works. It's smart only to read the first char of the string.

I will be back after some testing.... :)
Thomas Jensen

There are two types of people.
1) Those who can extrapolate from incomplete data

lianergoist
Posts: 33
Joined: Sat Nov 08, 2014 12:38 pm
Location: Horsens, Denmark

Re: How do I debug stdin?

Fri Aug 10, 2018 8:57 pm

rpdom wrote:
Fri Aug 10, 2018 6:47 pm
In psuedo-code:

Code: Select all

loop_start:
    read character from stdin
if character == "\n" go to loop_start
"\n" is the escape sequence that matches the LF character (0x0a ascii)
Well, I know that much. I think I am a little confused about why it should do what my book said it was supposed to do (jump over the LF character). I have now been running the code in gdb, and I can see the loop does (of course) nothing in my code, so .... I have to studie the code in the book again.

Well, jahboater showed a way to get around the problem with getline not waiting for keyboard input. But, my guess is, it is not waiting because it receives a LF from fgets (???) and think work is done. I would very much like to understand why. How can I test if there is a LF hanging? I use fflush, but that's not working...

If someone can tell me how to view stdin in gdb, I would also very much like to know. I have tried 'print stdin, but it just return this:

Code: Select all

(gdb) print stdin
$1 = (struct _IO_FILE *) 0x76f9d640 <_IO_2_1_stdin_>
and I don't know how to get a readable value out of that...
Thomas Jensen

There are two types of people.
1) Those who can extrapolate from incomplete data

jahboater
Posts: 2913
Joined: Wed Feb 04, 2015 6:38 pm

Re: How do I debug stdin?

Fri Aug 10, 2018 11:38 pm

lianergoist wrote:
Fri Aug 10, 2018 8:57 pm
If someone can tell me how to view stdin in gdb, I would also very much like to know. I have tried 'print stdin, but it just return this:

Code: Select all

(gdb) print stdin
$1 = (struct _IO_FILE *) 0x76f9d640 <_IO_2_1_stdin_>
and I don't know how to get a readable value out of that...
stdin is a pointer to a structure.

So try 'print *stdin' and see what that gets you.

The structure is actually defined in /usr/include/libio.h (not /usr/include/stdio.h like it used to be).
look for the line starting with "struct _IO_FILE".

jahboater
Posts: 2913
Joined: Wed Feb 04, 2015 6:38 pm

Re: How do I debug stdin?

Fri Aug 10, 2018 11:39 pm

lianergoist wrote:
Fri Aug 10, 2018 8:57 pm
If someone can tell me how to view stdin in gdb, I would also very much like to know. I have tried 'print stdin, but it just return this:

Code: Select all

(gdb) print stdin
$1 = (struct _IO_FILE *) 0x76f9d640 <_IO_2_1_stdin_>
and I don't know how to get a readable value out of that...
stdin is a pointer to a structure.

So try 'print *stdin' and see what that gets you.

The structure is actually defined in /usr/include/libio.h (not /usr/include/stdio.h like it used to be).
Look for the line starting with "struct _IO_FILE". There you will see the details of the structure with comments.

lianergoist
Posts: 33
Joined: Sat Nov 08, 2014 12:38 pm
Location: Horsens, Denmark

Re: How do I debug stdin?

Sat Aug 11, 2018 6:06 am

jahboater wrote:
Fri Aug 10, 2018 11:39 pm
So try 'print *stdin' and see what that gets you.
Well, I *did* know that, so this is a little embarrassing... :roll:

Well, I leaned something today. fflush has no effect on stdin...

Code: Select all

(gdb) next
Search another word? (y/n) 36				selected = fgetc(stdin);
(gdb) next
y
37			} while (selected == '\n');
(gdb) p *stdin
$4 = {_flags = -72539512, _IO_read_ptr = 0x22411 "\n", 
  _IO_read_end = 0x22412 "", _IO_read_base = 0x22410 "y\n", 
  _IO_write_base = 0x22410 "y\n", _IO_write_ptr = 0x22410 "y\n", 
  _IO_write_end = 0x22410 "y\n", _IO_buf_base = 0x22410 "y\n", 
  _IO_buf_end = 0x22810 "", _IO_save_base = 0x0, _IO_backup_base = 0x0, 
  _IO_save_end = 0x0, _markers = 0x0, _chain = 0x0, _fileno = 0, _flags2 = 0, 
  _old_offset = -1, _cur_column = 0, _vtable_offset = 0 '\000', 
  _shortbuf = "", _lock = 0x76f9e6c4 <_IO_stdfile_0_lock>, _offset = -1, 
  __pad1 = 0x0, __pad2 = 0x76f9d6e0 <_IO_wide_data_0>, __pad3 = 0x0, 
  __pad4 = 0x0, __pad5 = 0, _mode = -1, _unused2 = '\000' <repeats 39 times>}
(gdb) next
...
41				fflush(stdin);
(gdb) next
42				search_word();
(gdb) p *stdin
$5 = {_flags = -72539512, _IO_read_ptr = 0x22411 "\n", 
  _IO_read_end = 0x22412 "", _IO_read_base = 0x22410 "y\n", 
  _IO_write_base = 0x22410 "y\n", _IO_write_ptr = 0x22410 "y\n", 
  _IO_write_end = 0x22410 "y\n", _IO_buf_base = 0x22410 "y\n", 
  _IO_buf_end = 0x22810 "", _IO_save_base = 0x0, _IO_backup_base = 0x0, 
  _IO_save_end = 0x0, _markers = 0x0, _chain = 0x0, _fileno = 0, _flags2 = 0, 
  _old_offset = -1, _cur_column = 0, _vtable_offset = 0 '\000', 
  _shortbuf = "", _lock = 0x76f9e6c4 <_IO_stdfile_0_lock>, _offset = -1, 
  __pad1 = 0x0, __pad2 = 0x76f9d6e0 <_IO_wide_data_0>, __pad3 = 0x0, 
  __pad4 = 0x0, __pad5 = 0, _mode = -1, _unused2 = '\000' <repeats 39 times>}
(gdb) 
Thomas Jensen

There are two types of people.
1) Those who can extrapolate from incomplete data

jahboater
Posts: 2913
Joined: Wed Feb 04, 2015 6:38 pm

Re: How do I debug stdin?

Sat Aug 11, 2018 6:18 am

Perhaps there is simply no stuff in the buffer waiting to be read.
In which case fflush() would do nothing.

lianergoist
Posts: 33
Joined: Sat Nov 08, 2014 12:38 pm
Location: Horsens, Denmark

Re: How do I debug stdin?

Sat Aug 11, 2018 6:44 am

jahboater wrote:
Sat Aug 11, 2018 6:18 am
Perhaps there is simply no stuff in the buffer waiting to be read.
In which case fflush() would do nothing.
Perhaps? But isn't it clear there *is* something in the buffer "('y\n"), both before and after fflush? I would expect both _IO_read_end and (for sure) _IO_read_ptr to point to same address as _IO_read_base after fflush.
Thomas Jensen

There are two types of people.
1) Those who can extrapolate from incomplete data

jahboater
Posts: 2913
Joined: Wed Feb 04, 2015 6:38 pm

Re: How do I debug stdin?

Sat Aug 11, 2018 7:06 am

lianergoist wrote:
Sat Aug 11, 2018 6:44 am
jahboater wrote:
Sat Aug 11, 2018 6:18 am
Perhaps there is simply no stuff in the buffer waiting to be read.
In which case fflush() would do nothing.
Perhaps? But isn't it clear there *is* something in the buffer "('y\n"), both before and after fflush? I would expect both _IO_read_end and (for sure) _IO_read_ptr to point to same address as _IO_read_base after fflush.
Good point.
I don't know. Perhaps now is the time to download the source of glibc and look at the code for fflush?
http://ftp.gnu.org/gnu/glibc/
Look for "glibc-2.28.tar.xz" (or .gz)

I tend to just assume this stuff works, its been around for 50 years or so and is in constant use by millions of programs.

lianergoist
Posts: 33
Joined: Sat Nov 08, 2014 12:38 pm
Location: Horsens, Denmark

Re: How do I debug stdin?

Sat Aug 11, 2018 9:21 am

jahboater wrote:
Sat Aug 11, 2018 7:06 am
Perhaps now is the time to download the source of glibc and look at the code for fflush?
No need for that. A quick google search show tthe behavior of flushing stdin is undefined. See https://www.geeksforgeeks.org/clearing- ... fer-in-cc/
jahboater wrote:
Sat Aug 11, 2018 7:06 am
I tend to just assume this stuff works, its been around for 50 years or so and is in constant use by millions of programs.
*LOL* Oh yes. Well, I didn't thought I had found a bug in libc... :D
Thomas Jensen

There are two types of people.
1) Those who can extrapolate from incomplete data

jahboater
Posts: 2913
Joined: Wed Feb 04, 2015 6:38 pm

Re: How do I debug stdin?

Sat Aug 11, 2018 1:17 pm

Quite right - I should have looked at the standard first too :(
C17 says:

Code: Select all

Description
2 If stream points to an output stream or an update stream in which the most recent
operation was not input, the fflush function causes any unwritten data for that stream
to be delivered to the host environment to be written to the file; otherwise, the behavior is
undefined.
3 If stream is a null pointer, the fflush function performs this flushing action on all
streams for which the behavior is defined above.
I didn't know you could do "fflush(NULL)" ......

Return to “C/C++”

Who is online

Users browsing this forum: JulianG9 and 7 guests