When I launch my program using backticks like:
cat `./my_program`
I expect my read() to read character by character, it still reads line by line. The non-canonical mode of my terminal doesn't work. I don't really understand why.
Here is my code:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <term.h>
#include <strings.h>
int main()
{
struct termios term_setting;
char *term;
char buff[10];
term = getenv("TERM");
tgetent(NULL, term);
tcgetattr(STDOUT_FILENO, &term_setting); //
term_setting.c_lflag &= ~(ICANON | ECHO); //set my term in echo and non-canonique mode
tcsetattr(STDOUT_FILENO, 0, &term_setting); //
for (;;){
bzero(buff, sizeof(char) * 10);
read(STDIN_FILENO, buff, 9);
printf("%s\n", buff);
}
return (0);
}
There seems to be some confusion here: what do you expect
cat `./my_program`
to produce?
my_program copies standard input to standard output, in an infinite loop, even after reaching the end of file, which you do not test, producing unexpected output.
The shell collects this output either in a file, in a pseudo-terminal or possibly via a pipe and, once completed, passes it to cat as command line arguments, but since you must kill my_program in order to complete its output, the shell will abort this operation too.
cat expects options and filenames as command line arguments, probably not what you type.
Regarding the change of settings for the terminal, you could use STDIN_FILENO or just 0 instead of STDOUT_FILENO, since you want to change the behavior of the input handle.
Ry-♦ say in comment:
Because stdout isn’t the terminal anymore. Did you mean to get/set those attributes on STDIN_FILENO?
It's exactly my probleme, thx!
Related
I'm following a tutorial for making a text editor .
So far it's been tinkering with raw mode . The following code is supposed to turn off canonical mode , and output each keypress.
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
struct termios orig_termios;
void disableRawMode() { … }
void enableRawMode() { … }
int main() {
enableRawMode();
char c;
while (read(STDIN_FILENO, &c, 1) == 1 && c != 'q') {
if (iscntrl(c)) {
printf("%d\n", c);
} else {
printf("%d ('%c')\n", c, c);
}
}
return 0;
}
I originally forgot to add "\n" after the printf() statements, and the result was that I only got the outputted characters after the program terminates , ie after pressing q in this example .
However after adding "\n", the terminal outputs each letter as pressed.
Could anyone be so kind and explain why is it behaving this way?
Raw-mode is the concern of the terminal but buffer management of stdout occurs before reaching the terminal.
By default, when file-descriptor 1 (STDOUT_FILENO) is link to a terminal, then stdout uses a line-buffering policy.
This means that the output buffer of stdout is flushed to the file-descriptor 1 when a \n is written (or when it is full).
Only at this moment the characters can reach the terminal which can react in different ways depending on its configuration.
In your example, the characters just stay in memory until the process terminates (stdout is flushed at this moment).
Commonly, when a C program starts with the standard output stream connected to a terminal, the stream is line buffered. This means characters printed with printf or standard library methods are kept in a buffer until \n is printed (ending the line, hence “line buffered”), the buffer is full, when the stream is manually flushed (as with fflush), or when input is solicited on stream that is unbuffered or that is line buffered but requires characters from “the host environment” (notably a human).
The terminal setting is irrelevant as the characters are kept in an internal buffer of the standard C library implementation and are not sent to the terminal until one of the above events.
You can set the stream to unbuffered by calling setvbuf(stdout, NULL, _IONBF, 0) before performing any other operation on stdout.
I am writing a C program which takes user input. The user input requires some special characters which I want the reserve special keys on the keyboard for.
To keep it simple, suppose I want any occurrence of the symbol \ to be replaced with λ. So that if the user types \x.x, they see λx.x.
To clarify, I don't want their input to be repeated back to them with \ replaced by λ, I want them to enter \ but see λ immediately in the console.
Is there an easy way to do this?
Edit: Since it seems something like this is OS specific, I'd like a unix/linux solution.
I think this is a great question with many applications! For this, termios.h is better than curses.h in my opinion because with termios.h you can input to the terminal as you normally would, without requiring a fullscreen application like curses seems to. Also, you do not need to compile with a library (curses requires -lcurses option in your compiler). Note that this solution requires you to implement your own getch-like function. In addition, this solution is linux specific (AFAIK)
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <locale.h>
#include <wchar.h>
wint_t mygetwch()
{
// Save the current terminal details
struct termios echo_allowed;
tcgetattr(STDIN_FILENO, &echo_allowed);
/* Change the terminal to disallow echoing - we don't
want to see anything that we don't explicitly allow. */
struct termios echo_disallowed = echo_allowed;
echo_disallowed.c_lflag &= ~(ICANON|ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &echo_disallowed);
// Get a wide character from keyboard
wint_t wc = getwchar();
// Allow echoing again
tcsetattr(STDIN_FILENO, TCSANOW, &echo_allowed);
// Return the captured character
return wc;
}
int main()
{
// Imbue the locale so unicode has a chance to work correctly
setlocale(LC_ALL, "");
// Start endless loop to capture keyboard input
while (1) {
wint_t wc = mygetwch(); // get a wide character from stdin
if (wc==WEOF) // exit if that character is WEOF
break;
else if (wc==L'\\') // replace all instances of \ with λ
wprintf(L"%lc",L'λ');
else // otherwise just print the character
wprintf(L"%lc",wc);
}
return 0;
}
Is there an easy way to do this?
Yes, it's easy with a readline macro mapping \ to λ. Demo program:
/* cc -lreadline */
#include <stdio.h>
#include <stdlib.h>
#include <readline/readline.h>
main()
{
// bind the backslash key to lambda's UTF-8 code 0xCE 0xBB (cebb)
rl_parse_and_bind((char []){"\"\\\\\":'\xCE\xBB'"});
unsigned char *line, *cp;
while (cp = line = readline("? "))
{
while (*cp) printf("%3x", *cp++); puts("");
free(line);
}
}
Of course a UTF-8 enabled terminal is needed for the display of λ.
How can I remove a character on the terminal before the cursor in Linux? In the past I used something like this:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define KEY_BACKSPACE 127
int main(){
printf("%s", "abc"); // add something so we can see if delete works
char * buf = malloc(3*sizeof(char));
*(buf+0)=KEY_BACKSPACE;
*(buf+1)=' ';
*(buf+2)=KEY_BACKSPACE;
write(1,buf,3);
free(buf);
}
This is only a small example demonstrating this technique. In the original program I disabled canonical mode and handled every keystroke myself. That's why I needed to remove characters.
Writing backspace, space, backspace worked fine in my original program. Now when I run same program after a few years, it didn't remove anything. What changed? What can I do to fix this?
As I noted in a comment, you need to use backspace instead of '\177' (or '\x7F') to move backwards. You also have to worry about buffering of standard I/O. It's often best not to use a mixture of standard I/O and file descriptor I/O on the same stream — standard output in this example. Use one or the other, but not both.
This works:
#include <unistd.h>
int main(void)
{
char buff1[] = "abc";
char buff2[] = "\b \b";
write(STDOUT_FILENO, buff1, sizeof(buff1) - 1);
sleep(2);
write(STDOUT_FILENO, buff2, sizeof(buff2) - 1);
sleep(2);
write(STDOUT_FILENO, "\n", 1);
return 0;
}
It shows first (for 2 seconds):
abc
then (for another 2 seconds):
ab
then it exits. The cursor is after c at first, then after b.
As explained by Jonathan Leffler in the comment, your code needs two modifications:
The rubout character understood by the typical terminal (emulator) is '\b' (or 8), not 127.
printf() is line-buffered by default when writing to a TTY. This means that you need to call fflush(stdout) between calls to printf() and write(). Without flushing abc will only be printed at program exit, so the deletion sequence will be emitted before the contents it is supposed to delete, which renders it inoperative.
Hi i was reading K & R C where in the example to use getchar we have to press RET to end the input. Is there any way to change the input so that I can change the newline as the input separator to a comma. I use a Linux Mint . 64 bit. I get the input by running the program as ./Hello.o
Eg.
Hello world<RET>
but as I type a comma or dot the input should end
Eg.
Hello world, //End of input due to comma
Is there any way to change new line to another character to comma
It depends what function you want to use. For example, if you want to use getdelim you can provide a delimiter argument
ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream);
for example:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char * lineptr = malloc(1000 * sizeof(char));
size_t n = 1000;
/*type something in that ends in a comma*/
getdelim(&lineptr, &n, ',', stdin);
/*print the result*/
printf("%s\n", lineptr);
free(lineptr);
return 0;
}
Note that this still requires you to press enter but everything after the comma will be discarded.
edit
Maybe you could try something like this?
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
/* Use this variable to remember original terminal attributes. */
struct termios saved_attributes;
void
reset_input_mode (void)
{
tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
}
void set_input_mode (void)
{
struct termios tattr;
char *name;
/* Make sure stdin is a terminal. */
if (!isatty (STDIN_FILENO))
{
fprintf (stderr, "Not a terminal.\n");
exit (EXIT_FAILURE);
}
/* Save the terminal attributes so we can restore them later. */
tcgetattr (STDIN_FILENO, &saved_attributes);
atexit (reset_input_mode);
/* Set the funny terminal modes. */
tcgetattr (STDIN_FILENO, &tattr);
tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
tattr.c_cc[VMIN] = 1;
tattr.c_cc[VTIME] = 0;
tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
}
int main()
{
char c;
set_input_mode ();
while (1)
{
read (STDIN_FILENO, &c, 1);
if (c == ',')
break;
else
putchar (c);
}
return 0;
}
This program will receive input until a comma. I can't take any credit for it, I found this link on noncanonical input.
You can use getch() function available in ncurses library.
You do no need to press Enter after each character input, therefore you can scan input in a while loop using getch() unless you get a , or any special character you want.
I would first read the standard input line by line using getline(3). Then you can do your own tokenizing on that line, e.g. with sscanf(3), strtok(3) or other means (e.g. using strchr(3) appropriately). See this answer for some code (but getline won't enable you to avoid pressing the return key, because the kernel tty subsystem is processing that key, unless you do raw keyboard input which is really difficult).
On Linux, you might be interested in using readline(3) from the readline library (which is quite powerful, so learn more about it). Maybe you could use the ncurses library.
Be aware that terminals are very complex things (or abstractions), mostly for historical reasons. Read with care the tty demystified page, and the Keyboard and Console HowTo.
Avoiding pressing the enter or return key is surprisingly difficult on Unix systems (and probably on others too!) because the kernel (and not only the application or its libc i.e. <stdio.h> functions) is usually buffering the input line. To avoid that, you need to do very low level and difficult programming (which could take you several weeks of work). Read first Advanced Linux Programming.
So instead of doing all the difficult coding by yourself, take several days to learn how to use readline (or maybe ncurses). Even with the help of such libraries, it is not that easy!
I am writing a C program on unix which should redirect it's output to the file, and write to it some text every second in infinite loop:
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(void) {
int outDes = open("./output.txt", O_APPEND | O_WRONLY);
dup2(outDes, 1);
while(1) {
printf("output text\n");
sleep(1);
}
}
But it writes nothing to the output file. I tried to change the 'while' loop for 'for' with 10 loops, and I found that it writes all 10 lines to the file at once after the series ends. It is not very good for me, while I need to have an infinite loop.
When I'm not redirecting output, it is all ok, and new line appears every second on terminal.
I also tried to put one
printf("text\n");
before redirecting output to the file. Then the program wrote the lines to the file in real time, which is good, but wrote there the first (non redirected) line too. I don't want this first line in my output file, I don't understand how it could be written into file when output was not redirected yet (maybe redirect remained there since last run?), and how it could cause that the lines are suddenly written in real time.
Can anyone explain me how does it work?
You are not checking the return value of open() and dup2(). If either open() or dup2() failed, it won't write anything in output.txt.
if (outDes < -1) {
perror("open");
return 1;
}
if (dup2(outDes, 1) == -1) {
perror("dup2");
return 1;
}
stdio streams are buffered, and the writes happen in memory before being done on the real file description.
Try adding a fflush(stdout) after printf().
You're running afoul of a poorly documented DWIMmy feature in many Unix C libraries. The first time you write to stdout or stderr, the library probes the underlying file descriptor (with isatty(3)). If it's a (pseudo-)terminal, the library puts the FILE in "line buffered" mode, meaning that it'll buffer input until a newline is written and then flush it all to the OS. But if the file descriptor is not a terminal, it puts the FILE in "fully buffered" mode, where it'll buffer something like BUFSIZ bytes of output before flushing them, and pays no attention to line breaks.
This is normally the behavior you want, but if you don't want it (as in this case), you can change it with setvbuf(3). This function (although not the behavior I described above) is ISO standard C. Here's how to use it in your case.
#include <stdio.h>
#include <unistd.h>
int
main(void)
{
if (freopen("output.txt", "a", stdout)) {
perror("freopen");
return 1;
}
if (setvbuf(stdout, 0, _IOLBF, 0)) {
perror("setvbuf");
return 1;
}
for (;;) {
puts("output text");
sleep(1);
}
/* not reached */
}