I'm working on a small C program for a college assignment and I've noticed a weird bug in my code. I use an iMac with the short keyboard generally, but its battery was flat so i plugged in a standard USB keyboard with number pad.
The weird thing is that if I hit [Enter] on my number pad, it seems to do what the regular [Enter} key does, but the \n I am trying to detect in the stdin function I made to read the keyboard input, doesn't work when I use the number pad's [Enter] key.
Wtf?
Here is my function that reads the user input:
/* This is my implementation of a stdin "scanner" function which reads
* on a per character basis until the the termination signals are found
* and indescriminately discarding all characters in the input in excess
* of the supplied (limit) parameter. Eliminates the problem of 'left-over'
* characters 'polluting' future stdin reads.
*/
int readStdin(int limit, char *buffer)
{
char c;
int i = 0;
int read = FALSE;
while ((c = myfgetc(stdin)) != '\n' && c != '\0') {
/* if the input string buffer has already reached it maximum
limit, then abandon any other excess characters. */
if (i <= limit) {
*(buffer + i) = c;
i++;
read = TRUE;
}
}
/* clear the remaining elements of the input buffer with a null character. */
for (i = i; i < strlen(buffer); i++) {
*(buffer + i) = '\0';
}
return read;
}
/* This function used to wrap the standard fgetc so that I can inject programmable
* values into the stream to test my readStdin functions.
*/
int myfgetc (FILE *fin) {
if (fakeStdIn == NULL || *fakeStdIn == '\0')
return fgetc (fin);
return *fakeStdIn++;
}
NB: The myfgetc and the subsequent *fakeStdIn are part of a way that I can unit test my code and 'inject' items into the stdin stream programatically as someone suggested on this question: How do I write a testing function for another function that uses stdin input?.
What output do you get for this tiny test?
#include <stdio.h>
int main(int argc, char* argv[]) {
int c;
while((c=getchar()) != EOF) {
printf("%d\n", c);
}
return 0;
}
Could well be that on Mac, you are getting \r\n, not just \n.
So it turns out that it's a Mac OSX thing. I've spoken to other Mac users and they have the same problem. Never found a fix because one may simply not exist. The problem doesn't occur on Solaris machines and since that's the OS which the code will be run on, I guess it doesn't really matter.
I am going to answer this myself with the answer that its just one of those OSX "quirks" and be done with it.
Related
I was reading BeeJ's C programming guide and copied his readline() function, that reads a line from stdin. Due to the way it's implemented it has no problem reading multibyte characters, as it reallocates the spaces in accordance with the total amount of received bytes, and as such, it has no problem with unicode input. Here's a program with the function included:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define printPrompt printf("db > ")
/* The readLine function, allocates memory for a short string and
** reads characters into it. When the string's size limit is met,
** the same memory block is reallocated, but twice the size.
** Shamelessly stolen from BeeJ's guide to C programming |=
*/
char* read_line(void) {
int i = 0; /* Position of the current character */
int linbuf = 4; /* Size of our line in memory, will be
duplicated once the line length surpasses it */
char* lin; /* The pointer value to our line */
int c; /* The value we'll use to accept characters */
if( !(lin = malloc( linbuf*sizeof(char))) )
return NULL;
while( c = getchar(), c != '\n' && c != EOF ) {
/* Check if the amount of bytes accepted has surpassed the
* amount of memory we've allocated so far */
if(i == linbuf - 1) {
/* If it did, reallocate double the space */
linbuf *= 2;
char* tmpbuf = realloc(lin, linbuf);
/* If the space couldn't have been allocated then we'd
* run out of memory. Delete everything and abort. */
if(tmpbuf == NULL) {
free(tmpbuf);
return NULL;
}
/* If we've arrived here that means there were no
* problems, so we'll assign the newly reallocated
* memory to "lin" */
lin = tmpbuf;
}
/* Add the new character to our allocated space */
lin[i++] = c;
}
/* If we've received an EOF signal after having read 0
* characters, we'd like to delete our allocated memory and
* return a NULL */
if(c == EOF && i == 0) {
free(lin);
return NULL;
}
/* Here we'll shrink the allocated memory to perfectly fit our
* string */
if(i < linbuf - 1) {
char* tmpbuf = realloc(lin, i + 1);
if(tmpbuf != NULL)
lin = tmpbuf;
}
/* Here we'll terminate the string */
lin[i] = '\0';
/* Finally, we'll return it */
return lin;
}
int main(int argc, char* argv[]) {
char* hey = read_line();
printf("%s\n", hey);
return 0;
}
An input of
Hello, World! (:
would result in the output of
Hello, World! (:
An input of multibyte characters such as
שלום, עולם! (:
would result in the correct output of
שלום, עולם! (:
However, if I were to press the backspace key, it would only delete a one byte character, resulting in garbled output; an input of (backspaces marked as \b):
שיהיה לכם בוקר טוב\b\b\b\b\b\b\b\bערב טוב
which is supposed to end up being:
שיהיה לכם ערב טוב
actually ends up being:
�שיהיה לכם בוק�ערב טוב
My computer runs a Musl-libc version of Void Linux, and I compiled the program with both tcc and gcc, both yielding the same results.
Does this problem have to do with my libc, with my terminal (suckless st), with my kernel, or is it something I'm missing in the code? Whatever might be the case, is there any way I can handle it, preferably without using any external libraries such as ICU or what have you?
"is there any way I can handle it [...] without using any external libraries" The answer is a big fat no. Unless you are prepared to write a big and complex library yourself, that is.
With external libraries this is trivial:
sudo apt install libreadline-dev # no idea how to say that in Void
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h> // optional, to enable line history
int main()
{
using_history(); // optional
char* s;
while ((s = readline("Type something > ")))
{
printf("You have typed: %s\n", s);
add_history(s);
}
printf ("Bye!\n");
}
With this, you get a bunch of goodies for free, including full Unicode-aware line editing, programmable keybindings, and input history.
Edit On a machine I first checked this, your program behaved like you describe. But on another machine, which happens to be my home desktop, it works as expected, no weird backspace behaviour at all. I checked both X11 terminals and the text linux tty. So I guess there is something with some kernels and/or terminals after all.
Later edit There is an stty setting that controls this behaviour, at least for UTF-8.
stty iutf8
and your program should behave as expected, with no big fat libraries needed.
What I intend to do is to get the character entered and used it as a pattern. I've tried using getchar() but it won't work. I've hear of using scanf but it skips and stops whenever I press "shift" for the special characters on my keyboard.
int i, j, n;
char c;
c = getchar();
printf("Enter value of n: ");
scanf("%d", &n);
printf("Enter a Character: ");
getchar();
for(i=1; i<=n; i++)
{
for(j=1; j<=i; j++)
{
printf("%c", c);
}
printf("\n");
}
You need to assign the value returned by getchar to the variable c, and you had a redundent call to getchar that's why it skips reading the desired input:
int i, j, n;
char c;
printf("Enter value of n: ");
scanf("%d", &n);
printf("Enter a Character: ");
scanf(" %c", &c);
for(i=1; i<=n; i++)
{
for(j=1; j<=i; j++)
{
printf("%c", c);
}
printf("\n");
}
You can use %c with scanf:
scanf("%d %c", &n, %c);
This eliminates the need for the two getchar calls.
The space is required; it tells scanf to skip whitespace.
The problem you have is that your assumptions on getchar(3) are incorrect. You think getchar() is going to return the next key pressed in the input stream, but you are incorrectly assuming that it will be done without buffering or system processing (the terminal driver gives the program complete lines, or even worse, if you are reading from a file, complete buffer blocks, that have to be buffered so you miss no characters from the input stream)
You are assuming incorrectly that the end of line you need to press for the input to be feeded to the program does not count in the input stream.
What actually happens is:
you feed a complete line (because the kernel driver works that way) so you press your character, and then you see nothing, not after you have pressed the return key.
once you press it, you have more than one character (depending on how many you pressed before hitting the return key) that will stay in the buffer, until they are so consumed by the program. Normally this happens when you have executed more getchar() or scanf() statements.
The idea of this buffering mechanism is to allow a programmer to process character by charcacter large amounts of text, without the overhead of making a system call per character reading (this is a costly operation) so think of getchar() not as a sample function to get new users introduced to the world of programming, but as a hint to experienced programmers to use efficiently without having to think on buffering large amounts of text.
With stdio package, every character counts, so you have to think slowly and minuciously when you feed input to getchar(3).
The next question is: Right, then how can I solve and stop my program until I press some key? The first answer, with the set of tools you have exposed here is, be careful on what you input, instead of asking for any key, ask the user to press the return key, and then, do something like:
printf("Hit <ENTER> to continue"); fflush(stdout); /* so we get the line out, bypassing the buffering mechanism */
int c;
while ((c = getchar()) != EOF && c != '\n') {
/* just ignore the character we have received */
}
/* c == '\n' || c == EOF, so we can continue */
or, if you prefer, you can write a function just to do this (as there can be so many criteria to implement it, nobody included such a function in the standard C library, my apologies for that. ;) )
void wait_for_enter()
{
/* I use stderr, for two reasons:
* o stderr is normally unbuffered, so there's no need to fflush()
* o stdout can be redirected, so the prompt will not be visible in
* case you want to save the output of your program.
*/
fprintf(stderr, "Hit <ENTER> to continue");
int c;
while ((c = getchar()) != EOF && c != '\n') {
/* just ignore the character we have received
* until we get the end of file (ctrl-d at the terminal)
* or a new line */
}
/* c == '\n' || c == EOF, so we can continue */
/* it's assumed that the user pressed the enter key, so the echoed
* enter already did a newline, no need to do it here */
} /* wait_for_enter */
In order to wait for any character and in raw mode, you need first to ensure your input comes from a terminal (you cannot do the following on a normal file), then you have to switch the terminal driver to raw mode, so each character is given immediately to the program and no line editing processing is done, and then set the stdin descriptor to no buffering at all. Only then, you can receive individual characters with getchar(3), one by one, as they are keyed in. I think this is far out of the scope of this question, as the code to do that is far more complex than the above.
EDIT
Following is a complete sample of a program that uses raw input to process characters as they are keyed in.
/* pru.c -- program to show raw input from the terminal.
* Author: Luis Colorado <luiscoloradourcola#gmail.com>
* Date: Fri Sep 20 08:46:06 EEST 2019
* Copyright: (C) 2019 Luis Colorado. All rights reserved.
* License: BSD.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h> /* see termios(3) for a description on terminal conf */
#define F(_fmt) __FILE__":%d:%s: " _fmt, __LINE__, __func__
/* this function switches the terminal into raw mode and returns a malloc(3)ed
* terminal configuration, so it can be later restored. BEWARE that the returned
* configuration info must be deallocated by free(3) once it's not needed anymore.
* In case of failure of any system call, the function returns NULL, and errno is
* set to the failing cause. */
struct termios *set_raw(int fd)
{
struct termios *ret = malloc(sizeof *ret), cfg;
if (!ret) return NULL;
int res = tcgetattr(fd, &cfg);
if (res < 0) goto error;
*ret = cfg; /* save it for return */
cfmakeraw(&cfg);
/* set it after all buffered characters in the driver have drained out */
res = tcsetattr(fd, TCSADRAIN, &cfg);
if (res < 0) goto error;
return ret;
error:
free(ret);
return NULL;
} /* set_raw */
/* restores the configuration back to the associated file descriptor */
int restore_cfg(int fd, struct termios *cf)
{
/* set it after all buffered characters in the driver have drained out */
return tcsetattr(fd, TCSADRAIN, cf);
} /* restore_cfg */
int main()
{
struct termios *cfg = set_raw(fileno(stdin));
if (!cfg) {
fprintf(stderr, F("stdin: %s\n"),
strerror(errno));
}
setbuf(stdin, NULL); /* stdin unbuffered */
setbuf(stdout, NULL); /* stdout unbuffered */
/* BEWARE that raw mode doesn't process any characters, so no Ctrl-C(interrupt), Ctrl-D(EOF), etc.
* will be available, only if you read from a file, you'll get EOF, but you'll not be able to produce
* that on the terminal, you'll need to swith to another console and kill the process. */
int c;
while ((c = getchar()) != EOF && c != '\033') { /* ESCAPE key('\033') is a safeguard to end input */
/* print the input char as an hex number */
printf("[%02x]", c);
}
if (cfg) { /* if we were able to set the terminal to raw mode */
/* restore config */
restore_cfg(fileno(stdin), cfg);
/* and free it */
free(cfg);
}
exit(EXIT_SUCCESS);
} /* main */
The full source code can be also downloaded from here.
You can use this program to see how input keys get mapped into characters, as you'll note that when you press the enter key, the raw input is [0d] (ascii char 13, CARRY RETURN) while in normal line mode you get '\n' which is [0a] or ASCII LINE FEED, instead (you can check this if you redirect input from the pru.c text file). Also you'll see that you are unable to specify EOF from the terminal driver with Ctrl-D and that Ctrl-C does not come to help. Well, I have included a safeguard, by ending the program in case you press the ESC key, which generates an ASCII ESCAPE character (\033). This is also commented in the source code.
All of this processing is done by the kernel driver, so all unix implementations get the same line end characters or interpret the control characters the same way.
I have a C console application under Linux (Raspbian - Raspberry Pi). The program has some character animation - it prints out messages character by character. For example, the text
Please give me your name!
is printed out completely after 5 seconds (char by char).
Afterwards, the user is asked to type in their name, which works just fine if the user waits for the message to be printed out. However, if the user is impatient and hits the keys on the keyboard randomly, the input request later is compromised.
Imagine the user types in
abc\nefgh\n
while the above text is being echoed ('\n' means new line - enter). If that happens, the user will not be asked to properly type in his/her name, but the array of characters 'abc' will be accepted as input and gets validated.
My question is how to disable input (buffering) temorarily (on Linux). I have tried several methods and read numerous posts about doing so, but none of them worked for my purpose.
The function that is responsible for asking for input is as follows:
int getLine(char *s, int length)
{
int i;
char c;
for (i = 0; i < length && (c = getchar()) != EOF && c != '\n'; i++)
{
if (c == '\0')
{
i--;
}
else
{
s[i] = c;
}
}
s[i] = '\0';
while (c != EOF && c != '\n')
{
c = getchar();
}
return i;
}
Empty lines are eliminated with the help of a while loop in the function calling the one above so enter inputs are considered invalid.
I tried closing stdin, but could not reopen it:
fclose(stdin);
and emptying buffer before the getLine:
char buf[BUFSIZ];
while (c = fgets(buf, BUFSIZ, stdin) != NULL);
Unfortunately, it did not work as it does with text files.
fflush(stdin); did not work either and is not pretty anyway.
My goal is to prevent users from typing in anything while the text is being written out and to ignore/close input buffering (stdin) temporarily. Also, it would be great to disable outputting (flushing) user inputs during printing as it gets displayed on Linux terminal.
You may do this by interacting with TTY directly. Look into source code of passwd or similar utilities for inspiration.
Function below clears TTY input buffer (error handling omitted). You need to call it just before reading user input.
#include <sys/ioctl.h>
#include <termios.h>
void clear_user_input() {
if (isatty(STDIN_FILENO)) {
int fd = open(ttyname(STDIN_FILENO), O_RDONLY);
ioctl(fd, TCFLSH, TCIFLUSH);
close(fd);
}
}
I'm working with Code::Blocks and the thing is I tried many times to fix the problem with the Conio library and probably some other libraries as well. Every time I use something like clrscr(); textcolor(); or anything it says ;
Undefined reference to textcolor.
For example, this simple program is supposed to show the sum in a specific color but it's not working out though I have seen it work before.
#include <stdio.h>
#include <conio.h>
int fx(int x,int y,int z)
{
return x+y+z;
}
int main()
{
int a,b,c;
printf("Enter three values to a, b and c.\n");
scanf("%d%d%d",&a,&b,&c);
int total=fx(a,b,c);
textcolor(14);
printf("Output ="); cprintf(" %d",&total);
getch();
return 0;
}
P.S.: I'm using GNU GCC. And sometimes when I select another compiler or just open Code::Blocks it says, "Some plugins are missing," or something like that.
Can anyone help??
conio.h is not supported with gcc.
conio.h is not supported by gcc. Here is an implementation of conio.h for gcc though.
conio.h is not supported in gcc. You may try the curses library, which supports creation of text-user interface.
There are many flavor of curses, you may use ncurses or pdcurses library with code-blocks.
Some of the functions in the original Borland conio.h are easy to duplicate -- I've recently been porting from Turbo-C programs (from 1990!) to gcc, and found versions of getch and getche (for Linux) that I could use online (but not the C++ version, which won't compile using the gcc command). I wrote my own version of cgets, but haven't found the need to create my own versions of the other functions from that header file yet.
char getch()
{
char c; // This function should return the keystroke without allowing it to echo on screen
system("stty raw"); // Raw input - wait for only a single keystroke
system("stty -echo"); // Echo off
c = getchar();
system("stty cooked"); // Cooked input - reset
system("stty echo"); // Echo on - Reset
return c;
}
char getche()
{
char c; // This function should return the keystroke, with echo to screen
system ("stty raw"); // Raw input - wait for only a single keystroke
c = getchar();
system ("stty cooked"); // Cooked input - reset
return c;
}
char *cgets(char *buf)
/* gets a string from console and stores it in *buf; buf[0] must be initialized to maximum string size and *buf must
be declared by caller to maximum string size plus 3 bytes, to accommodate string, terminating null, size byte in buf[0]
and length of entered string in buf[1]; sets buf[1] to length of string entered and returns pointer to buf[2] */
{
/* declare and initialize internal variables */
unsigned int count = 2; /* start at 2 because [0] is max size including terminator and [1] returns actual */
/* entry size, also including terminating null */
char input = '\0'; /* initialize to null */
/* start actual function */
while (count < buf[0] + 2) /* while within permitted string length -- +2 for size control bytes */
{
input=getch(); /* get a single character, without echo */
if (input != (char) 13) /* not cr/enter key -- presumed meaningful input */
{
printf("%c",input);
buf[count++] = input; /* store character and increment counter */
}
else
{
buf[count] = '\0'; /* change cr/enter key to terminating null */
buf[1]=(char) count - 2;/* store length of entered string (including terminating null) */
count = buf[0] + 2; /* terminate entry loop -- +2 for size control again */
}
}
return &buf[2]; /* return pointer to start of string */
}
The key thing to remember is that an included file (such as conio.h) doesn't have to be precompiled; it can be just as functional if it's just more C source code.
Try this library: https://sourceforge.net/projects/coniohcloneturboccpp/.
The CONIO features for Windows and Linux are almost complete. It seems to work well.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Using fseek with a file pointer that points to stdin
i have a program that use fseek to clear my input buffer, it works well in Windows, buf fails in Linux. Please help me .
#include <stdio.h>
#define NO_USE_FSEEK 0
int main(int argc, char *argv[])
{
char ch = 'a';
int i = 1;
long int fpos = -1;
while(1)
{
printf("loop : %d\n", i);
fseek(stdin, 0L, SEEK_END); /*works in Windows with MinGW, fails in Linux*/
fpos = ftell(stdin);
if (-1 == fpos)
{
perror("ftell failure:"); /*perror tells it is Illegal Seek*/
printf("\n");
}
else
{
printf("positon indicator:%ld\n", fpos);
}
scanf("%c", &ch);
printf("%d : %c\n", (int)ch, ch);
i++;
}
return 0;
}
Thanks in advance!
This is not the accepted way to "clear your input buffer" on either Windows or Linux.
On windows, using the MSVCRT version of the standard C functions, there is an extension allowing fflush(stdin) for this purpose. Note that on other systems this is undefined behavior.
Linux has a function called fpurge with the same purpose.
However, I have to ask, why do you want to clear your input buffer? If it's the usual complaint people have with scanf not reading to the end of the line, it would be better to write code to actually read and discard the rest of the line (loop with getc until reading a '\n', for example, as in pmg's answer). Clearing the input buffer will tend to skip a large amount of data when used on a redirected file or pipe rather than the normal console/tty input.
i guess fseek will not work with stdin. Because the size of stdin is not known.
Test the return value from fseek() (in fact, test the return value from all <stdio.h> input functions).
if (fseek(stdin, 0, SEEK_END) < 0) { perror("fseek"); exit(EXIT_FAILURE); }
Use the idiom
while ((ch = getchar()) != '\n' && ch != EOF) /* void */;
/* if (ch == EOF)
** call feof(stdin) or ferror(stdin) if needed; */
to ignore all characters in the input buffer up to the next ENTER (or end of file or input error).