I'm trying to emulate a terminal using a C program in Linux and need my program to display a custom prompt while the program executes. Is there a way to display it using my C program? (I can always try to printf "My-prompt" every line manually, but I'm looking for a better way). Also I can't use any additional libraries other than the basic ones so GNU Readline library and editline library wouldn't work (as suggested in another thread).
for example:
user#mypc:~$ ./a.out
my_custom_prompt>3+5
my_custom_prompt>8
my_custom_prompt>exit
user#mypc:~$
I believe what the OP wants is to simply have the "prompt" printed along with any program output, without having to add this manually every time. There is a way to do this, if you write a wrapper function on top of printf to do this, and call that instead of printf directly.
Probably this will help: http://www.ozzu.com/cpp-tutorials/tutorial-writing-custom-printf-wrapper-function-t89166.html
In your example, you already have got a terminal. You want to write a command-line interface with a prompt, not a terminal.
I can always try to printf "My-prompt" every line manually, but I'm looking for a better way
There’s nothing wrong with this approach. You have a loop which prints the prompt and waits for input afterwards. As Kunerd said in the comment, one line of code.
Normally, a prompt is printed to stderr rather than stdout. This has the advantage, that the prompt appears before a newline is written, as stderr is unbuffered (and in combination with piping and redirection it seems reasonable to me, that this stuff doesn’t go to the same stream as the actual output).
Also I can't use any additional libraries other than the basic ones so GNU Readline library and Editline library wouldn't work
Doing this in a way strictly conforming to the C standard and not using any libraries but the standard one makes things like line editing (other than using backspace) or a command history (close to) impossible. If that’s OK for you, look for fgets etc. and keep in mind, that stdin is usually line-buffered.
POSIX specifies some additional properties of terminals, see e.g. http://pubs.opengroup.org/onlinepubs/9699919799/. Maybe curses is also of interest for you.
Perhaps you're looking for fgets() documentation?
Related
I would like to know if there is a way to print text into shell's current buffer/cursor so it can be edited. I am building a program that will store some text values in memory and need a simple way to edit them in the shell without rewriting the whole value. So somehow referencing the current edit buffer in shell and printing to it would be quite nice.
However, I am only using common sense here. Maybe it is more complicated. Looking forward to possible solutions.
Every shell handles user input differently. If there is an "edit buffer", it is most likely to be implemented in the shell itself. (Linux terminals do have a primitive line-editing function but as far as I know, there's no way to inject data into the line. However, only very primitive shells rely on native line-editing.)
So the question must be asked with respect to some specific shell.
Just in case, here's the answer for bash.
Bash relies on the readline library to perform command input from a terminal(-like) standard input. (Command-line history is provided using a related history library.) These libraries have a lot of features and bash does not provide access to all of them; if necessary, you could write a program in C or some scripting language with a readline binding.
But bash does give you the tools necessary to preload input with a line of text. The standard way of collecting input from a shell script is through the read command, which is (almost) always a shell "built-in". Basic ooeration of read is defined by the Posix standard, but the bash version provides a lot of useful extension options, including:
-e: use the readline library (with tab-completion, history and line editing enabked).
-i text: if readline is being used, preload readline's edit buffer with the specified text.
The
bash manual has lots more information about the read command's options.
Noob Q, best-practices related:
I'm writing a MS BASIC interpreter for macOS using yacc/lex/C. It uses scanf/printf for PRINT and INPUT. I would like to add a CLI option to redirect those to files.
I know how to do this technically, using fopen/fclose and fprintf etc.. But is that how "real" Unixen programs would do it? I would like to be as standard as possible to avoid confusion when someone else examines the code.
On generic Unix/Linux/FBSD, would you...
replace all the printf with fprintf(fp, and default fp to stdout?
keep all the printfs but redirect stdout?
rely on the shell and piping and not offer this as a CLI option at all?
(For a general program)
There's nothing wrong with simply relying on normal redirection by the parent (like the shell), but if your want to provide a parameter for that (which is also perfectly fine).
As for using printf or fprintf, I don't see much difference. I would probably choose one or other depending on the typical case. If it was almost always output on stdout, would probably use printf and fprintf otherwise. The part that may be confusing is that another person reading your code may not realize that printf isn't the program standard output (as for the redirection, you can use freopen or -before using stdio- dup2(, 1);).
(For your BASIC interpreter)
In this case, as you are translating INPUT and PRINT, scanf and printf seem the logical choices, and I'm not sure why you would need an option to change them to something else.
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,
I am writing a program that's similar to a shell. Once started up, there's a prompt and you enter in some app-specific commands.
So far this works just fine. However, I want to add support for command history like in Bash, so the user could hit the up or down arrow and see previously entered commands.
I have included the ncurses library, and I have done a simple hello world test with getch() to make sure the up and down arrows are reported correctly.
The thing that's bothering me is that it seems to be a requirement that I call initscr() which will clear the screen in order for me to use getch().
OKAY SO THE QUESTION IS:
Does anybody know a way to use ncurses getch() function without calling initscr() first? If not, can I make it not clear the screen? Basically, I'm looking to have getch() act the same as getchar(), if that makes sense.
Thanks in advance!
EDIT: I think the best example of this is how Python runs in interactive mode.
Curses wants to fully control the screen, and to optimize writes to the screen, especially over slow serial lines. To do this, it needs to know what is on the screen, and the only reasonable way to do that with most terminals is to start from an empty screen and keep track of what you write to the terminal.
Thus, I suspect (n)curses is the wrong tool for your shell. You probably need to go down a step on the abstraction layer and use terminfo and non-blocking reads from the terminal (standard input) instead.
(This is not very helpful. Sorry.)
It might be simpler to use an interface like readline() rather than resorting to full-blown ncurses.
You could just call your program from within rlwrap and have the functionality without the pain...
Here is another discussion about this. The provided solutions are:
"The 'filter()' function lets you use curses as a single-line."
"You can write something equivalent in C, using setupterm to get the
terminal data, and tparm, tputs for formatting and output."
Of course there is the third option to get the ncurses source code and modify it so it doesn't clear the screen anymore.
Have you considered the simple expedient of creating a custom terminfo or termcap entry lacking a sequence to clear the screen and then switching your terminal setting to that right before running your program? You could also just use newterm() and set_term() in ncurses. This used to be easier with termcap, since you could include another terminal and override some of its capabilities.