I'm creating a program to implement a linux shell
I've changed terminal mod into non-canonical
void ft_getch_prepare(void)
{
int ret;
struct termios new_opts;
ret = tcgetattr(STDIN_FILENO, &new_opts);
new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK
| ECHONL | ECHOPRT | ECHOKE | ICRNL);
new_opts.c_cc[VMIN] = 1;
new_opts.c_cc[VTIME] = 1;
ret += tcsetattr(STDIN_FILENO, TCSANOW, &new_opts);
}
int ft_getch(void)
{
int c;
c = 0;
ft_getch_prepare();
read(0, &c, 4);
return (c);
}
but when I want to copy a string and paste it, it only show the first character of the copied string
For example, I want to paste this string "HELLO WORLD" into my terminal, but
it only shows the first character "H"
If I complete your program with
int main()
{
int i = ft_getch();
printf("%x\n", i);
}
I get
$ ./a.out
4c4c4548
when I try to paste HELLO WORLD which is what I expect. (48 is the hexadecimal code for H, 45 for E, 4C for L; it look reversed as I'm on a little endian architecture).
The ICRNL flag constant applies to c_iflag, not c_lflag. You are turning it off in the wrong place. It is unclear to me why you're turning it off at all, but if you want to do so, then you need to modify the correct flag set.
The ECHOE, ECHOL, ECHONL, ECHOPRT, and ECHOKE local-mode flags are only relevant in canonical mode, which you are turning off. It should not be harmful to turn these off, too, but it does make your code harder to read and follow than it needs to be.
With respect to
when I want to copy a string and paste it, it only show the first character of the copied string
, I suspect you're being bitten by the input timer and / or minimum character count properties of non-canonical mode. These are controlled by the c_cc[VTIME] and c_cc[VMIN] elements of the "special characters" array in your termios structure. If you are configuring a terminal that will support interactive input, or for which there may otherwise be unbounded-length pauses in input, then you need to turn off the timer and ensure that reads block properly by setting
new_opts.c_cc[VTIME] = 0;
new_opts.c_cc[VMIN] = 1;
. I am uncertain whether that will be sufficient for your purposes, however, in part because I cannot judge whether the manner in which you are reading the input contributes to the issue.
UPDATE:
Since you've now disclosed your input function, I can say that you indeed do have significant problems there if it is supposed to provide an interface equivalent to getc(). You are reading four bytes at a time instead of one, and you are not handling EOF or errors properly. Moreover, the multi-byte read introduces the possibility of short reads, which you do not detect or handle.
If you're trying to read a single character at a time, then do that. The return value of getc() is int instead of char not because it's appropriate to try to read ints from the stream but to provide for result values that are not valid chars -- specifically, EOF.
I decline to rewrite your code for you, but to emulate getc(), it needs to do this:
read a single char at a time
check the return value of read. If it is anything other than 1 (for a one-character read) then return EOF
otherwise, return the char read, converted to type unsigned char.
Related
This is related to a stack smash attack.
Basically, I am trying to smash the stack by giving a program a particular input. The program takes in a user input like this, using getchar:
for (i = 0; (c = getchar()) != '\n'; i++) buf[i] = c;
I want to overwrite memory to become 0x000000a1. Unfortunately, 0xa1 is not an ascii character, so I cannot just input something like ยก (inverted exclamation) because that ends up giving 0x0000a1c2 in memory. How can I overwrite the value to be just 0x000000a1 without changing how the user input is processed in the program?
You can use bash to inject arbitrary characters:
echo -e '\xA1' | /path/to/program
You can add additional input, put the echo in a loop, etc.
echo -e 'Something\xA1\xA1\xA1' | /path/to/program
Your system's information is not provided, but usually the standard input is just a byte stream. It means that you can send arbitrary byte stream, not just valid characters.
For example, if your victim program is ./a.out, you can create a program to emit a payload
#include <stdio.h>
int main(void) {
putchar(0xa1);
putchar('\n'); /* to have the victim finish reading input */
return 0;
}
and compile to, for example, ./b.out and execute using a pipe
$ ./b.out | ./a.out
($ is your terminal's prompt)
I've used this small editor for the basis of my project that I'm doing: https://github.com/antirez/kilo
The editor uses the terminal in rawmode and writes using VT100 escape sequences, however when exiting the program the contents that were displayed, stay displayed.
Before exiting...
After exiting...
As you can see the prompt appears again but what was left of the editor stays there until written over.
// Low level terminal handling
void disable_raw_mode(int fd)
{
// dont bother checking the return value as its too late
if (Editor.rawmode) {
tcsetattr(fd, TCSAFLUSH, &orig_termios);
Editor.rawmode = 0;
}
}
void editor_at_exit(void)
{
disable_raw_mode(STDIN_FILENO);
}
int enable_raw_mode(int fd)
{
struct termios raw;
if(Editor.rawmode) return 0; //already enabled
if(!isatty(STDIN_FILENO)) goto fatal;
atexit(editor_at_exit);
if(tcgetattr(fd, &orig_termios) == -1) goto fatal;
raw = orig_termios; // modify the original mode
/* input modes: no break, no CR to NL, no parity check, no strip char,
* * no start/stop output control. */
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
// output modes - disable post processing
raw.c_oflag &= ~(OPOST);
//control modes - set 8 bit chars
raw.c_cflag |= (CS8);
//local modes, choing off, canonical off, no extended functions, no signal chars (, etc)
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
//control chars - set return condition: min number of bytes and a timer
raw.c_cc[VMIN] = 0; // return each byte, or zero for a timeout
raw.c_cc[VTIME] = 1; //100ms timeout
//put terminal in raw mode after flushing
if(tcsetattr(fd, TCSAFLUSH, &raw) < 0) goto fatal;
Editor.rawmode = 1;
return 0;
fatal:
errno = ENOTTY;
return -1;
}
From what I understand, when the program exits the atexit(editor_at_exit) function is called and in that function raw mode is disabled. What am I missing to clean the terminal back to what it was before the editor was opened. I'm not looking to just clear the whole terminal.
The functionality you are looking for is called "alternate screen buffer", which originated in xterm but is nowadays supported by most terminals.
Alternate screen buffer is designed to provide exactly this functionality for full-screen terminal programs. In normal operation, output gets added to the scrollback buffer (and most terminals let the user scroll back to previous lines). Switching to the alternate screen buffer the scrollback buffer is left alone, and alternate screen buffer output is not added to the scrollback buffer. When returning from alternate screen buffer, the original scrollback buffer state is restored. This is what full-screen applications like nano use.
To switch to the alternate screen buffer, I recommend writing (the C string)
"\033[?1049h\033[2J\033[H" (15 chars)
to the terminal. If the terminal emulator supports the alternate screen buffer, this changes to it, clearing it and moving the cursor to the upper left corner. If the terminal emulator does not support it, this will clear the screen and move the cursor to the upper left corner.
To return from the alternate screen buffer, I recommend writing (the C string)
"\033[2J\033[H\033[?1049l" (15 chars)
to the terminal. If the terminal emulator supports the alternate screen buffer, this first clears the alternate screen buffer, then returns to the original scrollback buffer (like e.g. nano does). If the terminal emulator does not support it, this will clear the screen and move the cursor to the upper left corner.
I recommend this pair ("\033[?1049h\033[2J\033[H" and "\033[2J\033[H\033[?1049l"), because it works in a reasonable fashion regardless of whether the terminal emulator supports the alternate screen buffer or not, not leaving the full-screen application state visible afterwards.
If standard input is a terminal, I also recommend using e.g.
int write_term(const char *p)
{
const char *q = p;
ssize_t n;
int retval = 0, saved_errno;
/* Nothing to write? */
if (!q || !*q)
return 0;
saved_errno = errno;
/* async-signal safe version of q = p + strlen(p) */
while (*q)
q++;
while (p < q) {
n = write(STDIN_FILENO, p, (size_t)(q - p));
if (n > 0) {
p += n;
} else
if (n != -1) {
retval = EIO;
break;
} else
if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
retval = errno;
break;
}
}
errno = saved_errno;
return retval;
}
to write the strings to the terminal, because standard C I/O functions may not be able to write to standard input (which is intended for reading only, after all). The above function is extremely careful, ignoring signal delivery (and even busy-looping if necessary if standard input is nonblocking), and even keeping errno intact; it is also async-signal safe, which means it could be used in a signal handler safely (although I advise against changing terminal buffer mode or settings in a signal handler, as that gets quite complicated to do correctly).
(The OP's code might have a suitable low-level I/O function already implemented, but one was not shown in the question.)
So I have the following code which basically just reads characters user inputs and prints them until 'q' is entered.
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<termios.h>
int main(void) {
char c;
static struct termios oldtio, newtio;
tcgetattr(0, &oldtio);
newtio = oldtio;
newtio.c_lflag &= ~ICANON;
newtio.c_lflag &= ~ECHO;
tcsetattr(0, TCSANOW, &newtio);
printf("Give text: ");
fflush(stdout);
while (1) {
read(0, &c, 1);
printf("%c", c);
fflush(stdout);
if (c == 'q') { break; }
}
printf("\n");
tcsetattr(0, TCSANOW, &oldtio);
return 0;
}
In the beginning of the main function I turn off the canonical mode to make user able to see his input when he's giving it. I also turn off the echo so stuff like "^[[A" doesn't pop up when pressing the up arrow key for example. This works, but I'm also able to move the cursor to upper rows on a terminal window and that's not good. Is there a way fix this so that user can only move within the current row?
Another problem is the backspace. When I press it the program prints a weird symbol (which I assume is 0x7f) instead of erasing the character left to the cursor's current location. I should propably handle the backspace key output in the program somehow but I don't know how to do it since it's this weird hexadecimal number. Any tips for this?
One option I've also been thinking about to make this work is to use canonical mode so the arrow keys and backspace functionalities are automatically in use. However, canonical mode works line by line and so the text doesn't appear until "Enter" is hit by the user. So far, I haven't figured out any way to make user see his input while typing. Is this even possible?
And please, no ncurses or readline suggestions. I want to do this using termios.h.
have you looked into the man pages? (should be man termios or look somewhere online)
There I found the ECHOE flag which is said to have the following effect:
If ICANON is also set, the ERASE character erases the preceding input character, and WERASE erases the preceding word.
This should fix your backspace problem?
I also suggest, you have a look into the examples in the man page. E.g. you could do the following:
newtio.c_lflag &= ~(ECHO | ECHOE | ICANON);
...to set more than one flag at a time in only one line. I know the man pages are hard to read for beginners but you will get used to them and the more you use them, the more efficient they become for looking up C/POSIX-functions etc (just in case, you don't use them anyway).
The arrow-key-problem: Maybe you can try the cfmakeraw()-function; its description sounds promising. I haven't had time to investigate any further about the arrow keys. However, maybe you find something else useful in the man page.
BTW: termios looks interesting, I always wondered which functions certain command line programmes are using; learned something by your question, thanks!
EDIT
I've done some more research this weekend. The "strange" symbol printed when pressing the backspace key is quite easy to hide. It is the ASCII-value 0x7f. So add a simple
if (c == 0x7f) { continue; }
...to just ignore the backspace key. Or handle it in a way to remove the last character (see code example below).
This simple workaround doesn't work for the arrow keys though as they are no ASCII characters :/ However, these two topics helped me handling also this problem: topic 1 and topic 2. Basically pressing the arrow keys results in a sequence of a couple of chars being sent to stdin (see the second link for more information).
Here is my complete code which (I think) works the way you wish:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
int main(void) {
char c;
static struct termios oldtio, newtio;
tcgetattr(0, &oldtio);
newtio = oldtio;
newtio.c_lflag &= ~ICANON;
newtio.c_lflag &= ~ECHO;
tcsetattr(0, TCSANOW, &newtio);
printf("Give text:\n");
fflush(stdout);
while (1) {
c = getchar();
// is this an escape sequence?
if (c == 27) {
// "throw away" next two characters which specify escape sequence
c = getchar();
c = getchar();
continue;
}
// if backspace
if (c == 0x7f) {
// go one char left
printf("\b");
// overwrite the char with whitespace
printf(" ");
// go back to "now removed char position"
printf("\b");
continue;
}
if (c == 'q') {
break;
}
printf("%c", c);
}
printf("\n");
tcsetattr(0, TCSANOW, &oldtio);
return 0;
}
BTW you can get the complete escape sequences by the following code:
int main(void) {
char c;
while (1) {
c = getchar();
printf("%d", c);
}
return 0;
}
I think I don't have to say that this complete thing is quite a dirty hack and it's easy to forget handling some special keys. E.g. in my code I don't handle the page-up/down or home keys... --> the code above is far from complete but gives you a point to start. You should also have a look at terminfo which can provide you a lot of the necessary information; it should also help with a more portable solution. As you see, this "simple" thing can become quite complex.So you might rethink your decision against ncurses :)
Actually, for handling the arrow keys, you would have to implement a good-sized chunk of ncurses. There are pros/cons: the main drawback to using ncurses in a command-line application might be that it usually clears the screen. However, (n)curses provides a function filter. There is a sample program "test/filter.c" in the ncurses source which illustrates this by using the left-arrow key as an erase character, and passes the resulting line to system() to run simple commands. The sample is less than 100 lines of code -- simpler and more complete than the examples above, it seems.
I'm trying to understand how terminal I/O works.
When a terminal is placed in non-canonical mode like so (missing error handling):
struct termios term_original, term_current;
tcgetattr(STDIN_FILENO, &term_original);
term_current = term_original;
term_current.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO);
term_current.c_iflag &= ~(BRKINT | ICRNL | IGNBRK | IGNCR | INLCR | INPCK | ISTRIP | IXON | PARMRK);
term_current.c_oflag &= ~(OPOST);
term_current.c_cc[VMIN] = 1;
term_current.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSADRAIN, &term_current);
A simple read loop can read in the data generated by each button press like so:
char c;
while (read(0, &c, 1) != -1) { PRINT_CHAR(c); }
Now,
Pressing Esc on my keyboard generates: 0x1b.
Pressing F1 generates: 0x1b 0x4f 0x50.
Pressing F5 generates: 0x1b 0x5b 0x31 0x35 0x7e.
In terms of reading and processing this input, how does one determine where the output from one button press ends and the next one begins? I could find no discernible pattern, and the fact that Esc generates a single byte which is also identical to the first byte of output for most multi-byte generating button presses seems to suggest there is none. Is there some other mechanism for determining where the button boundaries are?
Programs rely on keys not being pressed too fast. If the delay is less than say 100ms, this is one key press; otherwise there are two separate events.
Yes program actually pause for some time after ESC is being pressed, in order to make sure it's ESC and no some other key. Sometimes this pause can be discerned with the naked eye.
Some programs recognize the ESCDELAY environment variable for fine-tuning this timing.
And yes this is not perfect, you can fool the system by pressing keys too fast.
Okay, thanks to n.m., I was set on the right track here.
Trying to read one byte at a time is incorrect. Rather one should attempt to read multiple characters at once.
Something like the following:
int r, i;
char buffer[10]; //10 chosen arbitrarily
while ((r = read(STDIN_FILENO, buffer, sizeof(buffer))) != -1)
{
printf("%d bytes: ", r);
for (i = 0; i < r; ++i) { PRINT_CHAR(buffer[i]); }
printf("\r\n");
}
In this case, the read() call will return as soon as a button is pressed, and will return the amount of bytes read. Now the bytes can be used to identify the button or character in question.
Pressing the top row of buttons using above loop, I'm seeing:
1 bytes: 1b
3 bytes: 1b 4f 50
3 bytes: 1b 4f 51
3 bytes: 1b 4f 52
3 bytes: 1b 4f 53
5 bytes: 1b 5b 31 35 7e
5 bytes: 1b 5b 31 37 7e
On my machine, I appear to be getting:
A single byte for ASCII characters.
0x1b as the first character, followed by other characters for special buttons (F1-F12, Up, Down, etc...).
Some other multi-byte sequence for non ASCII characters, which turns out to be the UTF-8 representation of the character in question.
I tried jamming down the buttons on my keyboard like a mad man, but the above loop was always able to identify correctly which bytes are a single unit.
However this may not work completely as desired on a heavily taxed machine, or over a buffered high latency network connection. Perhaps in those situations, more bytes from multiple latter button presses will have already found themselves in the terminal buffer, causing multiple buttons to appear as one.
In such a situation, there probably is no way to ensure errors won't occur, however they can be minimized. Single byte characters always appear to be in the range of 0x00-0x7F. Special buttons are always multi-byte and begin with 0x1B followed by something within 0x00-0x7F. Multi-byte characters are always in the range 0x80-0xFF. The UTF-8 encoding sequence also has the first byte indicate how many bytes are in the current character. Given this information, there's enough to ensure errors are minimal and do not propagate to upcoming reads unnecessarily.
Lastly, it's important to stress that what I described is for my machine (PC, classic US 101 keyboard, Terminal encoding set to UTF-8). A full program should minimally see what character encoding the terminal is using.
Ultimately, you must determine these by context. Based on the characters you receive after the escape, you can determine the overall sequence length of known sequences, then return to interpreting characters normally.
You should be able to look up the escape sequences for known terminals.
It is possible some of your function keys have locally configured expansions, especially if they do not match the codes for whatever variety of standard terminal is otherwise implemented.
I am creating a Linux terminal program using C.
I am trying to make a two digit code address a array location.
I don't want to have to hit enter after every two digit input, I want the input to just be sent to my buffer variable through scanf directly after to characters are entered.
I do not have a code sample, as i have no idea how to approach this.
Thanks for any help!
You've got two options, which solve the same problem in nearly the same way. The first is to use stdbuf when you run your program; the invocation is:
stdbuf -i0 ./a.out
Using that prevents stdin from being line-buffered, and will let you use fread() or similar commands to retrieve input as it happens.
The other is to put the terminal in raw mode. It's well-described here. But the downside is that control characters are no longer dealt with. In your program, you
#include <termios.h>
main(){
struct termios trm;
tcgetattr(STDIN_FILENO, &trm); /* get the current settings */
trm.c_cc[VMIN] = 1; /* return after 1 byte read; you might make this a 2*/
trm.c_cc[VTIME] = 0; /* block forever until 1 byte is read */
tcsetattr(STDIN_FILENO, TCSANOW, &trm);
}