Issue
I am looking at making a custom bash prompt using a C++ script. The plan is to call the script from within the PS1 declaration in .bash_profile like this:
PS1='$(bashprompt.out)'
This part works totally fine. The problem that I am running into is that the ANSI sequences are being printed to the terminal. You can see this in the following image, where it prematurely returns to the beginning of the line.
I am defining the colors in the format as follows:
#define DGRAYORANGE "\033[48;5;202;38;5;243m"
In a bash script, I know the way to avoid this issue is to denote the color as non-printing by using \[ \]
(like explained here https://www.funtoo.org/Prompt_Magic), but that does not work in C++.
This is what the prompt should look like:
Here is the C++ code that is being used to make the colored prompt:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <string>
#include <iostream>
#define ARROW ""
#define DGRAYORANGE "\033[48;5;202;38;5;243m"
#define ORANGEDGRAY "\033[38;5;202;48;5;243m"
#define DGRAYDEFAULT "\033[38;5;243;48;5;0m"
#define WHITEDGRAY "\033[38;5;255;48;5;243m"
#define WHITEORANGE "\033[38;5;255;48;5;202m"
#define RESET "\033[0m"
using namespace std;
void PrintPrompt(string, string, string);
string FormatCWD(char *);
int main() {
int buffersize = 256;
char hostname[buffersize];
char cwd[buffersize];
struct passwd* pw;
gethostname(hostname,sizeof(hostname));
pw = getpwuid(getuid());
getcwd(cwd,sizeof(cwd));
string stringCwd = FormatCWD(cwd);
string stringUsername = pw->pw_name;
string stringHostname = hostname;
PrintPrompt(stringCwd,stringHostname,stringUsername);
}
void PrintPrompt(string cwd, string hostname, string username){
string cwdString = cwd;
cout << WHITEDGRAY << username <<
DGRAYORANGE << ARROW << " " <<
WHITEORANGE << hostname <<
ORANGEDGRAY << ARROW << " " <<
WHITEDGRAY << cwd << DGRAYDEFAULT <<
ARROW << RESET << " " << endl;
}
string FormatCWD(char * cwd) {
string stringCwd = cwd;
int size = stringCwd.length();
int slashCount = 0;
int slashIndex = 0;
for (int i=0; i<size; i++) {
if (stringCwd.at(i) == '/') {
slashIndex = i;
slashCount++;
}
}
string outputCwd = "";
if (slashIndex != 0) {
for (int i=slashIndex+1; i<size; i++) {
outputCwd += stringCwd.at(i);
}
}
return outputCwd;
}
Is there any way to fix this issue by marking the escape sequences as non-printing (like in bash)? Thank you for any help you guys give.
Solution
Note first that \[
and \]
are interpreted by Bash itself, not the console, so they must be explicitly present in the PS1
value, not in the output of the program generating actual prompt.
Now, you want the non-escape chars to count, while colors are not to be counted. For this, you can modify your program to have two modes:
- Colored output (for actual prompt)
- Text-only output (for character counting)
Then you can use something like
PS1='$(/tmp/test)\[\r$(/tmp/test c)\]'
which would work for code modified to the following:
void PrintPrompt(string cwd, string hostname, string username, bool colors){
string cwdString = cwd;
cout << (colors ? WHITEDGRAY : "") << username <<
(colors ? DGRAYORANGE : "") << ARROW << " " <<
(colors ? WHITEORANGE : "") << hostname <<
(colors ? ORANGEDGRAY : "") << ARROW << " " <<
(colors ? WHITEDGRAY : "") << cwd << (colors ? DGRAYDEFAULT : "") <<
ARROW << (colors ? RESET : "") << endl;
}
with colors
being passed as e.g. argc>1
.
We use the \r
escape sequence to go to the beginning of the line, enabling us to overwrite our already printed non-colored text with the fancy-colored one, making Bash think that the additional text and escape sequences (including the carriage return) don't take any space.
Screenshot:
Answered By - Ruslan Answer Checked By - Dawn Plyler (WPSolving Volunteer)