For the sake of education, and programming practice, I'd like to write a simple library that can handle raw keyboard input, and output to the terminal in 'real time'.
I'd like to stick with ansi C as much as possible, I just have no idea where to start something like this. I've done several google searches, and 99% of the results use libraries, or are for C++.
I'd really like to get it working in windows, then port it to OSX when I have the time.
Sticking with Standard C as much as possible is a good idea, but you are not going to get very far with your adopted task using just Standard C. The mechanisms to obtain characters from the terminal one at a time are inherently platform specific. For POSIX systems (MacOS X), look at the <termios.h> header. Older systems use a vast variety of headers and system calls to achieve similar effects. You'll have to decide whether you are going to do any special character handling, remembering that things like 'line kill' can appear at the end of the line and zap all the characters entered so far.
For Windows, you'll need to delve into the WIN32 API - there is going to be essentially no commonality in the code between Unix and Windows, at least where you put the 'terminal' into character-by-character mode. Once you've got a mechanism to read single characters, you can manage common code - probably.
Also, you'll need to worry about the differences between characters and the keys pressed. For example, to enter 'ï' on MacOS X, you type option-u and i. That's three key presses.
To set an open stream to be non-buffered using ANSI C, you can do this:
#include <stdio.h>
if (setvbuf(fd, NULL, _IONBF, 0) == 0)
printf("Set stream to unbuffered mode\n");
(Reference: C89 4.9.5.6)
However, after that you're on your own. :-)
This is not possible using only standard ISO C. However, you can try using the following:
#include <stdio.h>
void setbuf(FILE * restrict stream, char * restrict buf);
and related functions.
Your best bet though is to use the ncurses library.
Related
Apologies if this is unclear, but I'm really not sure how to describe this.
See, my issue is that I'm trying to read from the text for user input using fgets(), however, I also need to know if the user presses a special key like /b (backspace) since ideally I want it to start deleting characters from the line before if the current line is empty, like a text editor, which isn't possible with C.
Anyways, let me know if you need more information, thanks in advance.
EDIT: Thought I'd go ahead and post what I have as of now in case someone comes across this later with the same issue to get a better idea
for (; ;)
{
int i;
int key = getch();
if (key == '/b')
{
printf("Hello World");
}
else
{
buffer[i++] = key; // adding character to text user is writing
}
i++;
}
note that this code doesn't work at the moment because of a linker error and something with the /b, but in essence, this could work.
EDIT 2: Thank you chqrlie for bringing up the right way to refer to special characters. Forgot you had to use the backslash for them.
You can certainly write text editors in C :-) A lot of them are.
However, you can't write them in portable C because the facilities required for interactive character-by-character terminal I/O don't exist in standard C. They don't exist because standard C doesn't assume that there is such a thing as a terminal (lots of embedded CPUs don't have any such thing, for example), and it would go far beyond the mandate of ISO C to try to standardise the disparate communications protocols of the various operating systems which do have terminal-like I/O.
getch is an obsolete interface in the Windows conio.h header, and also one of the interfaces in the Curses library, which is available for many operating systems (including Windows). It has nothing to do with fgets, which is part of standard C, and it cannot be implemented using fgets.
It seems likely that what you are trying to do could best be accomplished using an implementation of Curses. (If you're using Linux, as suggested by your avatar, you would be looking for Ncurses.) You might want to look at the Forms library built on top of Ncurses, which provides higher-level input facilities, like editable text boxes.
Another common input-handling library, GNU readline, might also be useful, but it doesn't handle multiline input forms so it would probably be a lot more work.
I'm brushing up on unix calls so this might seem a naive question (on vacation and just bored). I know that there's standard i/o in C but it always seems like the low-level calls (write, read, open) are used in practice for UNIX-like systems(just checked a couple of open-source projects). Is standard-io used in practice much? Are there cutoffs or specific reasons why the low-level api is used more? Or am I making a bad assumption from a few cherry-picked cases regarding low-level being more popular? I understand standard i/o is a C language element but seems like they achieve same thing and that low-level is used more.
The stdio(3) library don't cover all the abilities available on Linux. In particular, socket(2) and other low level functionalities (e.g. polling with poll(2) etc...) is not provided by <stdio.h> functions. However, <stdio.h> functions usually give buffering which is practically very important for performance reasons. So calling write(2) for every single byte would be very inefficient. Use fflush(3) to flush <stdio.h> buffers.
Read Advanced Linux Programming for more.
In practice, mixing <stdio.h> functions and low-level syscalls (like read(2), write(2), mmap(2), poll(2), fcntl(2) ...) with <stdio.h> functions is often (but not always) impractical. See also fileno(3). So people may choose to code at the syscall level.
However, when <stdio.h> functions are enough it is convenient to use them.
Also <stdio.h> is standardized in the C11 standard, but write etc... only in POSIX ....
FWIW, I tend to use stdio in 3 main areas:
(1) Where it is easy and practical to take advantage of stdio's buffering.
(2) text files where fgets and the like are more convenient to use than homegrown lower level functions which do the same thing.
(3) output formatting. fprintf when it is practical; sprintf and write when it isn't. I rarely use input formatting like fscanf but that might have more to do with the kinds of applications I encounter and when I do I usually try to wangle a way to write it in C++. (Totally personal preference.)
Thing is, in the "everything is (kind of) like a file" posix world, you tend to be using file descriptors for a lot of different calls so after awhile stdio becomes slightly cumbersome unless it offers something compelling. Those things, for me, are listed above.
So what I want is when the user presses the up button, I want to instantly display the command and not show the ^[[A I already know how to identify up and down from the keyboard, and I know termios might be the solution, but coming from a java background, I don't quite know how to do this. Ncurses is not an option, due to certain constraints. If you can, please help.
What I expect to happen is, when the user presses the up button, I want to instantly do a printf on the same line
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char input[100];
char *history[5];
int currentHistory=0;
int historyCount=5;
history[0]="hi";
history[1]="bye";
history[2]="computer";
history[3]="science";
history[4]="Yo";
while(input[0]!='Q')
{
if(input[0]==LEFT_ARROW;
}
}
You'll find this functionality, and more, in either the GNU readline or the BSD editline libraries. They offer largely the same functionality, however the GNU readline library is under a GPL license, and editline is under a BSD license.
It isn't clear if you want to handle your input better, or your terminal handling better.
If you want to handle input better, I suggest wrapping your character recogonition into a function that returns an integer, which has defines for each "handled" key. That way your input looks more like
int char = readChar(input);
if (char == KEY_UP_ARROW) {
...
}
where readChar detects if the first key is an escape and then "peeks" to see if there is extra information available (to differentiate between a stand alone escape and the arrow keys).
If you intend to handle the screen side of things better, and your intent is to drive the terminal with your own wrapped terminal handling software, you need to reference the VT100 terminal reference (assuming that your xterm emulates VT100 (which many do)). The VT100 codes are described here.
Note that the operating system does some extra processing on input (to combine key combinations, making programs easier to differentiate between deadkey keyboard setups, etc.), so you might to disable this in-terminal processing by putting the terminal in a different "mode". Typical modes include "cooked", and "raw", where "raw" provides you with key codes that are not combined for simplicity.
In my continuing attempt to understand how pseudo-terminals work, I have written a small program to try to run bash.
The problem is, my line-breaking seems to be off. (The shell prompt only appears AFTER I press enter.)
Furthermore, I still cannot properly use ncurses programs, like vi. Can anyone tell me how to setup the pseudo-terminal for this?
My badly written program can be found here, I encourage you to compile it. The operating system is GNU/Linux, thanks.
EDIT: Compile like this: gcc program.c -lutil -o program
EDIT AGAIN: It looks like the issue with weird spacing was due to using printf(), still doesn't fix the issue with ncurses programs though.
There are several issues in your program. Some are relatively easy to fix - others not so much:
forkpty() and its friends come from BSD and are not POSIX compatible. They should be avoided for new programs. From the pty(7) manual page:
Historically, two pseudoterminal APIs have evolved: BSD and System V. SUSv1 standardized a pseudoterminal API based on the System V API, and this API should be employed in all new programs that use pseudoterminals.
You should be using posix_openpt() instead. This issue is probably not critical, but you should be aware of it.
You are mixing calls to raw system calls (read(), write()) and file-stream (printf(), fgets()) functions. This is a very good way to confuse yourself. In general you should choose one approach and stick with it. In this case, it's probably best to use the low-level system calls (read(), write()) to avoid any issues that would arise from the presence of the I/O buffers that the C library functions use.
You are assuming a line-based paradigm for your terminals, by using printf() and fgets(). This is not always true, especially when dealing with interactive programs like vim.
You are assuming a C-style single-byte null-terminated string paradigm. Terminals normally deal with characters and bytes - not strings. And while most character set encodings avoid using a zero byte, not all do so.
As a result of (2), (3) and (4) above, you are not using read() and write() correctly. You should be using their return values to determine how many bytes they processed, not string-based functions like strlen().
This is the issue that, in my opinion, will be most difficult to solve: You are implicitly assuming that:
The terminal (or its driver) is stateless: It is not. Period. There are at least two stateful controls that I suspect are the cause of ncurses-based programs not working correctly: The line mode and the local echo control of the terminal. At least these have to match between the parent/master and the slave terminal in order to avoid various strange artifacts.
The control-interface of a terminal can be passed-through just by passing the bytes back and forth: It is not always so. Modern virtual terminals allow for a degree of out-of-band control via ioctl() calls, as described for Linux here.
The simplest way to deal with this issue is probably to set the parent terminal to raw mode and let the slave pseudo-terminal driver deal with the awkward details.
You may want to have a look at this program which seems to work fine. It comes from the book The Linux Programming Interface and the full source code is here. Disclaimer: I have not read the book, nor am I promoting it - I just found the program using Google.
I need to build a OS, a very small and basic one, with actually least functionality, coded in C.
Probably a CUI OS which does some memory management and has at least a text editor and a calculator, its just going to be a experimentation about how to make a code that has full and direct control over your hardware.
Still I'll be requiring an interface, that will need input/output functions like printf(&args), scanf(&args). Now my basic question is should I use existing headers or go for coding actually from scratch, and why so ?
I'd be more than very thankful to you guys for and help.
First, you can't link against anything from libc ... you're going to have to code everything from scratch.
Now having worked on a micro-kernel myself, I would not use the actual stdio headers that come with libc since they are going to be cluttered with a lot of extra information that will be either irrelevant for your OS, or will create compiler errors due to missing definitions, etc. What I would do though is keep the function signatures for these standard functions the same ... so in the end you would have a file called stdio.h for your OS, but it would be a very stripped down header file with the basic minimum requirements for your needs, and only having the standard I/O functions you need, with the correct standard signatures.
Keep in mind on the back-end, i.e., in your stdio.c file, you're going to have to point these functions to a custom console-driver or some other type of character drive for your display. Either that, or you could just use them as wrappers for some other kernel-level display printing routine. You are also going to want to make sure that even though you may use a #include <stdio.h> directive in your other OS code modules to access these printing functions, you do not link against libc. This can be done using gcc -ffreestanding.
Just retarget newlib.
printf, scanf, etc relies on implementation specific funcions to get a single char or print a single char. You can then make your stdin and stdout the UART 1 for example.
Kernel itself would not require the printf and scanf functions, if you do not want to keep the kernel in kernel mode and work the apps you have planned for. But for basic printf and scanf features, you can write your own printf and scanf functions, which would provide basic support for printing ans taking input. I do not have much experience on this, but you can try make a console buffer, where the keyboard driver puts the read in ASCII characters (after conversion from scan codes), and then make the printf and scanf work on it. I have one basic implementation were i have wrote a gets instead of scanf and kept things simple. To get integer output you can write an atoi function to convert the string to a number.
To port in other libraries, you need to make the components which the libraries depend on. You need to make the decision if you can code in those support in the kernel so that the libraries could be ported in. If it is more difficult then coding some basic input output functions i think won't be bad at this stage,