Michael
Michael

Reputation: 31

C++ output to windows terminal using cout<<term_cc<color, default, attrib> outputs colors and attributes properly on Windows but not on Linux

Thought I was done and ready to submit this little project until I got this unexpected curveball. The objective is to make a parser using a token lexer. Essentially <underline><red> R <green> G </green> <blue> B </blue> and back to red </red></underline> will output as: "RGB and back to red" in their respective colors and attributes.

Everything works fine on windows but when I moved it over to the Linux systems it outputs the color codes with nothing happening.

#include <iostream>
#include <sstream>
#include <stack>
#include <map>
#include <cstdlib>
#include <vector> 
#include "cmd.h"
#include "Lexer.h" // you should make use of the provided Lexer
#include "term_control.h"
#include "error_handling.h"

using namespace std;

map<string, term_colors_t> colorMap;    
map<string, term_attrib_t> attribMap;
string display(const string& expression) 
{
if(validate(expression) == "VALID") {
Lexer lex;
Token tok;

vector<term_colors_t> colorVect;
vector<term_attrib_t> attribVect;

lex.set_input(expression);
while(lex.has_more_token()){
    tok = lex.next_token();
    string sTok = tok.value;
        if(tok.type == TAG && tok.value.at(0) != '/'){
            cout<<term_cc(colorMap[tok.value], DEFAULT_COLOR, attribMap[tok.value]);
            colorVect.push_back(colorMap[tok.value]);
            attribVect.push_back(attribMap[tok.value]);
        }
        if(tok.type == TAG && tok.value.at(0) == '/'){
        colorVect.pop_back();
        cout<<term_cc(colorVect.back(), DEFAULT_COLOR, attribVect.back());
        }
        if(tok.type != TAG){
            cout<<tok.value;
        }
    }
}
else if(validate(expression) != "VALID")    return validate(expression);
return "";

}

_

    cout<term_cc(Color, DEFAULT_COLOR, Attribute)

is the specfic method where the problem is hiding I have been searching around and can't seem to find the proper method.

    cout<<term_fg(color)

that method properly displays color on the Linux system but I cannot have attributes with that method.

Everything I've been reading pertained only to color not color and attributes they were also using the echo command and hard coded colors for specific terminals. These would require serious changes in all my code and cause it to not work on Windows and only on Linux so I'm trying to avoid this.

Thanks in advance for any advice on this problem everyone I appreciate it hopefully I'll be able to get this in before 12!

Upvotes: 0

Views: 389

Answers (1)

rici
rici

Reputation: 241701

It's not clear to me where colorMap and attribMap are initialized and to what values, and I'm just going on instinct here, but it seems likely that the keys for colorMap are colours and the keys for attribMap are attributes. In that case underline is not a key in colorMap and red is not a key in attribMap.

In your program, you do the following:

if(tok.type == TAG && tok.value.at(0) != '/'){
        cout<<term_cc(colorMap[tok.value], DEFAULT_COLOR, attribMap[tok.value]);

which assumes that every TAG is present in both colorMap and attribMap. But if the tag is a colour like "red", it is (probably) only in colorMap and if it is an attribute like "underline", it is (probably) only in attribMap.

Now, what happens when you execute colorMap["underline"]? Here, the convenience of C++'s standard library can be a bit of a disadvantage because it silently hides an error. The answer is that a mapping from "underline" to the default value of a term_colors_t is added to the map so that the lookup will always return something. term_colors_t is an enum, so its default value is 0 (not '0').

Now, term_cc -- if it is the same term_cc that @MikePetch dug up -- does not check its arguments for validity; it just assumes that they are valid ANSI digits ('0' through '9', or in other words a number between 48 and 57, inclusive.) Since it doesn't check them, it just outputs includes them as they are in its output, and since you're (probably) calling term_cc with an attributes argument of 0 -- that is, a NUL character -- it outputs the NUL as part of the supposed console code.

I checked xterm, konsole and the Linux console, and all of them ignore the NUL character. (I believe that is the expected behaviour; DEC terminals like the VT-100 ignored NULs, although in some circumstances you needed to insert them because the terminal would also ignore any character if the previous control took too long.) I don't know what terminal emulator you are using, and it is quite possible that it has different behaviour, such as terminating the control code sequence. term_cc outputs the attribute first, even though it is the third argument, so it could well be that a NUL attribute would cause the terminal emulator to simply print something like ;31;49m instead of setting the foreground colour to red.

Some other bugs:

  • You never pop attribVect; only colorVect. So I don't see how the attributes will be properly restored.

  • You don't initialize colorVect to a DEFAULT_COLOR. So after the first tag is popped, you'll pop the (only) element off of colorVect, leaving it empty, and then call colorVect.back(), which is undefined if colorVect is empty.

Those were just the things I notice on a quick skim through the code. There might be other problems.

Upvotes: 1

Related Questions