Delete the newline symbol - c

I have a calculator application, which enquires the user to input an arithmetic expression like 10 + 15, follow it with an = sign, and press Enter. The program should evaluate the expression and print the result.
However, the answer is on a new line, even though I want it on the same one like
10 + 15 = 25
I tried to use ungetch as well but it didn't work.
So how can I remove that newline character to obtain the upper result?

If your terminal supports them, you can use terminal escape codes to move the cursor.
#include <stdio.h>
int main ( ) {
char input[200] = "";
int ch = 0;
int count = 0;
printf ( "\033[2J");//clear screen and move cursor to upper left corner
printf ( "\033[8;H");//move cursor to line 8
printf("Enter equation\n");//\n advances to line 9
while ( ( ch = getchar ( )) != '\n' && ch != EOF) {
input[count] = ch;//store input for processing
count++;//count characters
}
printf ( "\033[9;%dH", count + 1);//move cursor to row 9 col count + 1
printf ( "answer here\n");
return 0;
}

If you are reading a line of input from the console, the user can freely edit the text they type in until they press Enter. When they do press Enter, the cursor immediately moves to the start of the line, possibly scrolling the screen buffer up in the process.
To move the cursor back to the previous line, you will need to use a "curses" library that allows you to send cursor movement commands to move the cursor back up to the previous line, and then to the end of their input. This could be complicated if they used tab characters and/or typed in more than one line of input.
See "ncurses" or, on Windows, PDCurses.

If you're using a terminal it's not easy because it's typically waiting on newlines before it does anything. Have a look here...
setvbuf not able to make stdin unbuffered
If you are doing this on a socket or some other stream that you completely controle then you need to use unbuffered input. Have a look here
Buffered and Unbuffered inputs in C
Note, this will make your calculator a bit harder to write. Alternatively you could print the whole line ie input and answer in the terminal.

Related

fgets() always waiting for input, can't find how to fix it

I have an issue here were I can't find a way to stop fgets() to read from stdin after I paste my input in it. I want it to read what I pasted and stop after (it can be different length). Now it is waiting for another input.
void readMatrix(char (*charM)[40]) {
char charT[42] = {0};
int count = 0;
while (fgets(charT, 42, stdin) != NULL) {
for (int i = 0; i < 40; i++){
if (charT[i] == 0 || charT[0] == '\n' ) {
printf("Erreur: Caractère `EOF` inattendu, attendu `H`, `X`
ou `.`.");
exit(0);
}
if(charT[i] != '.' && charT[i] != 'H' && charT[i] != 'X') {
printf("Erreur: Caractère `%c` inattendu, attendu `H`, `X`
ou `.`.", charT[i]);
exit(0);
}
charM[count][i] = charT[i];
}
count++;
if (count == 20) break;
}
}
Thanks!
while (fgets(charT, 42, stdin) != NULL)
Even though you do not type in anything and you hit enter, it will feed a null-terminated string in C. You either need to change this condition, or alternatively you need to feed the standard input an End-of-Transmission character. For Linux it is Ctrl + D, for Windows it is Ctrl + Z.
When you copy and paste, there is nothing copied that ends the input.
Since there is nothing copied in that ends the input, fgets() not return NULL.
After doing the copy and paste, there may be a last line of input waiting to be read. In that case, it is necessary to hit the enter key, so fgets() will return that last line of input. If you're lucky, that step is not necessary (because a carriage return has been copied as the last character) but there is still nothing telling fgets() that input has ended.
The fact that everything has been copied does not tell fgets() that input has ended. The significance is that, if you wish, you will be able to repeatedly copy text into your program, as many times as you like.
To actually signal the end of input to your program, you need to manually do something to indicate that. For most flavours of unix, that is done using CTRL-D. For windows, it is CTRL-Z. Depending on how your program is run (e.g. in a terminal window or not) it may be necessary to manually do that twice (e.g. hit CTRL-Z twice under windows).
Note that it is a good idea to hit the enter key at least once before signally end of input. If there is an incomplete line waiting to be read (i.e. no newline yet) some keyboard or terminal drivers will discard the last line if they encounter end of input. For example, under windows, always hit then CTRL-Z (and, possibly, you will still need to enter CTRL-Z a second time).
Note: some programs do sensibly handle copy and paste. But those programs use a system-dependent function INSTEAD of fgets() so they can recognise the end of copy/pasted text. You can't emulate that behaviour using fgets(), hence the need for the user to do something.

How to get the cursor position in a C program using termcap, without writing a character?

I would like to know how to get the cursor position (x, y) in my program, without writing anything on the screen neither tracking it all the time.
I found out a way to get its position with this function (I don't check the return of read, write, etc here to write a smaller code on this subject but I do it in my program):
void get_cursor_position(int *col, int *rows)
{
int a = 0;
int i = 0;
char buf[4];
write(1, "\033[6n", 4); // string asking for the cursor position
read(1, buf, 4);
while (buf[i])
{
if (buf[i] >= 48 && buf[i] <= 57)
{
if (a == 0)
*rows = atoi(&buf[i]) - 1;
else
*col = atoi(&buf[i]) - 1;
a++;
}
i++;
}
}
This function gives me the exact cursor position (*rows = y, *col = x), but it writes on the screen.
How can I get the cursor position without writing anything on the screen?
(If the cursor is on one of the printed characters, it will overwrite it.)
Should echo be toggled before and after sending the escape sequence?
This is a school project, so I only can use termcap, I can't use ncurses functions, the only allowed functions are tputs, tgoto, tgetstr, tgetnum, tgetflag.
There are several problems:
canonical mode is buffered (see below)
the read is done on the file-descriptor for standard output (that may happen to work — sometimes — but don't count on it)
the read does not read enough characters to get a typical response
the response would have two decimal integers, separated by semicolon ;
the response would have a final character (which would become an issue if the read actually asked for enough characters...)
Further reading:
General Terminal Interface The Single UNIX ® Specification, Version 2
In canonical mode input processing, terminal input is processed in units of lines. A line is delimited by a newline character (NL), an end-of-file character (EOF), or an end-of-line (EOL) character. See Special Characters for more information on EOF and EOL. This means that a read request will not return until an entire line has been typed or a signal has been received. Also, no matter how many bytes are requested in the read() call, at most one line will be returned. It is not, however, necessary to read a whole line at once; any number of bytes, even one, may be requested in a read() without losing information.
XTerm Control Sequences
CSI Ps n Device Status Report (DSR).
Ps = 5 -> Status Report.
Result ("OK") is CSI 0 n
Ps = 6 -> Report Cursor Position (CPR) [row;column].
Result is CSI r ; c R
That is, your program should be prepared to read Escape[ followed by two decimal integers (with no fixed limit on their length), and two other characters ; and R.
By the way, termcap by itself will do little for your solution. While ncurses has some relevant capabilities defined in the terminal database:
# u9 terminal enquire string (equiv. to ANSI/ECMA-48 DA)
# u8 terminal answerback description
# u7 cursor position request (equiv. to VT100/ANSI/ECMA-48 DSR 6)
# u6 cursor position report (equiv. to ANSI/ECMA-48 CPR)
few programs use those, and in any case you would find it difficult to use the cursor position report in a termcap application.

Controlling getchar() in while loop

Say I have a simple while loop to enter **1*0* characters.
After more than 10, I want the loop to stop.
However, the break seems not to take effect.
Only when I press Enter it ends. Can anyone explain please?
int count = 0;
int numchars = 10;
ch = getchar();
while( ch != '\n' && ch != '\0' ) {
array[count] = ch;
count++;
if ( count > numchars ){
break;
}
ch = getchar();
}
Thanks.
stdin is not your tty, even when your tty is connected to stdin. Unless you put your tty in raw mode, the program does not see any data at all until you hit return. When you hit return, all of the data on the line is sent to the program, which then enters the loop and reads characters until it breaks out of the loop. If you really want the program to see characters as you press them, you will need to do a lot more work. Look into libraries like ncurses first, and then do some research on how to put a tty into raw mode. And then write the simple program that requires the user to hit return.
use getch() or getche() instead of getchar() and numchars=9 for 10 characters.

Why does this getchar() loop stop after one character has been entered?

#include <stdio.h>
int main() {
char read = ' ';
while ((read = getchar()) != '\n') {
putchar(read);
}
return 0;
}
My input is f (followed by an enter, of course). I expect getchar() to ask for input again, but instead the program is terminated. How come? How can I fix this?
The Terminal can sometimes be a little bit confusing. You should change your program to:
#include <stdio.h>
int main() {
int read;
while ((read = getchar()) != EOF) {
putchar(read);
}
return 0;
}
This will read until getchar reads EOF (most of the time this macro expands to -1) from the terminal. getchar returns an int so you should make your variable 'read' into an integer, so you can check for EOF. You can send an EOF from your terminal on Linux with ^D and I think on windows with ^Z (?).
To explain a little bit what happens. In your program the expression
(read = getchar()) !='\n'
will be true as long as no '\n' is read from the buffer. The problem is, to get the buffer to your program, you have to hit enter which corresponds to '\n'.
The following steps happen when your program is invoked in the terminal:
~$\a.out
this starts your program
(empty line)
getchar() made a system call to get an input from the terminal and the terminal takes over
f
you made an input in the terminal. The 'f' is written into the buffer and echoed back on the terminal, your program has no idea about the character yet.
f
f~$
You hit enter. Your buffer contains now 'f\n'. The 'enter' also signals to the terminal, that it should return to your program. Your progam
reads the buffer and will find the f and put it onto the screen and then find an '\n' and immediatley stop the loop and end your program.
This would be standard behaviour of most terminals. You can change this behaviour, but that would depend on your OS.
getchar() returns the next character from the input stream. This includes of course also newlines etc. The fact that you don't see progress in your loop unless you press 'Enter' is caused by the fact that your file I/O (working on stdin) doesn't hand over the input buffer to getchar() unless it detects the '\n' at the end of the buffer. Your routine first blocks then handles the two keystrokes in one rush, terminating, like you specified it, with the appearance of '\n' in the input stream. Facit: getchar() will not remove the '\n' from the input stream (why should it?).
after f you are putting "enter" which is '/n'.
so the loop ends there.
if you want to take another character just keep on putting them one after the other as soon as enter is pressed the loop exits.
You've programmed it so the loop ends when you read a \n (enter), and you then return 0; from main which exits the program.
Perhaps you want something like
while ((read = getchar()) != EOF) {
putchar(read);
}
On nx terminals you can press Control-D which will tell the tty driver to return the input buffer to the app reading it. That's why ^D on a new line ends input - it causes the tty to return zero bytes, which the app interprets as end-of-file. But it also works anywhere on a line.

GNU Readline: how to clear the input line?

I use GNU Readline in the "select" fashion, by registering a callback function like so:
rl_callback_handler_install("", on_readline_input);
And then hooking up rl_callback_read_char as the callback for my select() loop for STDIN_FILENO. That's all pretty standard stuff, and works fine.
Now, my program asynchronously prints messages to the screen, sometimes interleaved with input from the user. A "clean" session would look like this:
user input
SERVER OUTPUT
SERVER OUTPUT
user input
SERVER OUTPUT
But what if the user is midway through a line when the server response arrives? Then it gets ugly:
user input
SERVER OUTPUT
user inSERVER OUTPUT
put
SERVER OUTPUT
I fixed this simply by printing a newline before the server output if the user had typed anything (this is easy to tell by checking rl_line_buffer), and then doing rl_forced_update_display() after printing the server output. Now it looks like this:
user input
SERVER OUTPUT
user in
SERVER OUTPUT
user input
SERVER OUTPUT
This is better, but still not perfect. The problem comes when the user typed an entire line but didn't yet press Enter--then it looks like this:
user input
SERVER OUTPUT
user input
SERVER OUTPUT
user input
SERVER OUTPUT
This is bad because it appears to the user that they typed three commands (three responses for three inputs is just as possible as three responses for two inputs, which is what actually happened).
A nasty hack (which works) is to do this:
user input
SERVER OUTPUT
user input - INCOMPLETE
SERVER OUTPUT
user input
SERVER OUTPUT
I figured I could improve this by printing backspace ('\b') characters instead of " - INCOMPLETE", but that doesn't seem to do anything at all on my terminal (gnome-terminal on Ubuntu Hardy). printf("ABC\b"); just prints ABC, for whatever reason.
So how can I erase the incomplete input line? Either by printing backspaces somehow (I can figure out how many to print--it's strlen(rl_line_buffer)), or by using some Readline facility I don't yet know about?
After quite a lot of hacking I was able to get this mechanism. I hope other people will find it useful. It does not even use select(), but I hope you will get the point.
#include <readline/readline.h>
#include <readline/history.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
const char const* prompt = "PROMPT> ";
void printlog(int c) {
char* saved_line;
int saved_point;
saved_point = rl_point;
saved_line = rl_copy_text(0, rl_end);
rl_set_prompt("");
rl_replace_line("", 0);
rl_redisplay();
printf("Message: %d\n", c);
rl_set_prompt(prompt);
rl_replace_line(saved_line, 0);
rl_point = saved_point;
rl_redisplay();
free(saved_line);
}
void handle_line(char* ch) {
printf("%s\n", ch);
add_history(ch);
}
int main() {
int c = 1;
printf("Start.\n");
rl_callback_handler_install(prompt, handle_line);
while (1) {
if (((++c) % 5) == 0) {
printlog(c);
}
usleep(10);
rl_callback_read_char();
}
rl_callback_handler_remove();
}
With spaces? Try to print "\b \b" for each character you want to "delete" rather than a single '\b'.
Edit
How it works
Suppose you have written "Hello, world!" to the display device and you want to replace "world!" with "Jim."
Hello, world!
^ /* active position */ /* now write "\b \b" */
/* '\b' moves the active position back;
// ' ' writes a space (erases the '!')
// and another '\b' to go back again */
Hello, world
^ /* active position */ /* now write "\b \b" again */
Hello, worl
^ /* active position */ /* now write "\b \b" 4 times ... */
Hello,
^ /* active position */ /* now write "Jim." */
Hello, Jim.
^ /* active position */
Portability
I'm not sure, but the Standard specifically describes the behaviour of '\b' and '\r' as has been described in answers to your question.
Section 5.2.2 Character display semantics
> 1 The active position is that location on a display device where the next character output by
> the fputc function would appear. The intent of writing a printing character (as defined
> by the isprint function) to a display device is to display a graphic representation of
> that character at the active position and then advance the active position to the next
> position on the current line. The direction of writing is locale-specific. If the active
> position is at the final position of a line (if there is one), the behavior of the display devic e
> is unspecified.
>
> 2 Alphabetic escape sequences representing nongraphic characters in the execution
> character set are intended to produce actions on display devices as follows:
> \a (alert) Produces an audible or visible alert without changing the active position.
> \b (backspace) Moves the active position to the previous position on the current line. If
> the active position is at the initial position of a line, the behavior of the display
> device is unspecified.
> \f ( form feed) Moves the active position to the initial position at the start of the next
> logical page.
> \n (new line) Moves the active position to the initial position of the next line.
> \r (carriage return) Moves the active position to the initial position of the current line.
> \t (horizontal tab) Moves the active position to the next horizontal tabulation position
> on the current line. If the active position is at or past the last defined horizontal
> tabulation position, the behavior of the display device is unspecified.
> \v (vertical tab) Moves the active position to the initial position of the next vertical
> tabulation position. If the active position is at or past the last defined vertical
> tabulation position, the behavior of the display device is unspecified.
>
> 3 Each of these escape sequences shall produce a unique implementation-defined value
> which can be stored in a single char object. The external representations in a text file
> need not be identical to the internal representations, and are outside the scope of this
> International Standard.
One thing you can do is to use \r to jump to the beginning of the line for the server output. Then you can use field width specifiers to right pad the output to the rest of the line. This will, in effect, overwrite whatever the user had already entered.
fprintf (stdout, "\r%-20s\n", "SERVER OUTPUT");
You may want to fflush(stdout) to ensure that the buffers are in a consistent state before you do that.
I tried to separate server output and user input with ncurses windows. Server output is simulated with a thread. The program run until You enter a line beginning with 'q'.
#include <unistd.h>
#include <curses.h>
#include <pthread.h>
WINDOW *top, *bottom;
int win_update( WINDOW *win, void *data ){
wprintw(win,"%s", (char*)data ); wrefresh(win);
return 0;
}
void *top_thread( void *data ){
char buff[1024];
int i=0;
while(1){
snprintf(buff, 1024, "SERVER OUTPUT: %i\n", i++ );
use_window( top, win_update, (void*)buff );
sleep(1);
}
return NULL;
}
int main(){
initscr();
int maxy, maxx;
getmaxyx( stdscr, maxy, maxx );
top = newwin(maxy-1,maxx,0,0);
wsetscrreg(top,0,maxy-1); idlok(top,1); scrollok(top,1);
pthread_t top_tid;
pthread_create(&top_tid, NULL, top_thread, NULL);
bottom = newwin(1,maxx,maxy-1,0);
char buff[1024], input[maxx];
do{
werase(bottom); wmove(bottom,0,0);
wprintw(bottom,"input> " ); wrefresh(bottom);
wgetnstr(bottom,input,sizeof(input));
snprintf(buff, 1024, "user input: '%s'\n", input );
use_window( top, win_update, (void*)buff );
}while( input[0] != 'q' );
endwin();
}
Do any of these functions help?
rl_reset_line_state()
rl_clear_message()
rl_delete_text()
rl_kill_text()
Also, can you mediate the server output - have the server output controlled so that it only appears when and where you want it to, rather than just sprawling over what the user is typing? For example, if your application is running in curses mode, could you have a split window with a line or two at the bottom in one sub-window reserved for user input and the rest of the output (server output and accepted user input) in a second sub-window above it?
This also seems to work:
rl_clear_visible_line();
printf(...);
rl_reset_line_state();
rl_redisplay();

Resources