Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Friday, March 6, 2009

Colors for Terminal Apps

This has been in my plans for a long time, but i had trouble learning how to do colored output in terminals correctly. I asked people, i googled, looked at terminfo, ncurses, but still could not find a satisfactory answer. So i started to look at other non-curses terminal apps, and i stopped already at the first one: git.

#include <unistd.h>
#include <stdlib.h>
#include <string.h>

bool has_colors()
{
if (::isatty(STDOUT_FILENO))
{
char *term = ::getenv("TERM");
if (term && ::strcmp(term, "dumb"))
return true;
}
return false;
}


This (translated to a C/C++ hybrid) is all that git (and now also zypper :O) does to detect the color capability of the stdout. stout must be a terminal, and the TERM environment variable must not be "dumb". The idea is that most color-capable terminals understand the ISO 6429 (former ANSI) color control sequences, and if the above check fails for your terminal, you can configure git (and soon zypper :O) to never or always use colors (or set the TERM to "dumb").

Regarding terminfo, i found it hard to learn and an overkill for just printing colored text (of course, once learned it can be used in other ways, too, e.g. to move the cursor, turn off input echoing when writing passwords, etc.). Maybe some other time.

On the other hand, ncurses are nothing for an existing terminal application which already does lots of output the usual way - you would have to rewrite all the output handling using ncurses. With ncurses it is that you write your app either with or without it from the start. But you cannot switch to it later, nor mix it with standard C I/O (printf() or C++ streams (cout <<). At least that's my impression from what i've learned...

So, zypper is now (since 1.1.0) a bit less boring, with colors :O) The idea is to print normal messages and progress in darker color, highlight important messages like results, warnings, errors, and color some special stuff, e.g. in install summary, default options in prompts, and so on. And it should be configurable, so that anybody can adapt it to his or her terminal background and text color. I'll write more about that once it's done.

Thursday, September 4, 2008

Convenient setters in C++?

Libzypp's RepoInfo class was originaly written with property setters returning a RepoInfo & reference to the modified object. This pattern is common in higher OO languages like Java and is convenient as it saves typing when setting serveral properties at once (i'm not sure how it is performance-wise).


RepoInfo repo;

// you write
repo
.setAlias("foo")
.setName("Foo Project Repository")
.setEnabled(true).setAutorefresh(false);

// instead of
repo.setAlias("foo");
repo.setName("Foo Project Repository");
repo.setEnabled(true);
repo.setAutorefresh(false);


Unless the class is part of a class hierarchy, everything's OK. But after splitting RepoInfo to RepoInfoBase and a derived RepoInfo the dark side of C++ has shown up.


RepoInfo repo;
repo
.setAlias("foo") // RepoInfoBase setter
.setEnabled(true) // RepoInfoBase setter
.setPriority(50); // RepoInfo setter - this won't compile becase setEnabled() returned
// a RepoInfoBase &, and C++ does not bother with figuring out that the
// object is also a RepoInfo.


In order to make this work in this setup, you have to redefine each setter from the base class in the derived class and make it return the reference to the derived class:


class RepoInfoBase
{
RepoInfoBase & setAlias( ... ) { _pimpl->alias = ...; return *this; }
}

class RepoInfo : public RepoInfoBase
{
RepoInfo & setAlias( ... )
{ RepoInfoBase::setAlias(...); return *this; }
}


but by doing this the RepoInfo class looses some of the advantages of being derived from RepoInfoBase.

So, is it best to stick with setters returning void, or is there a better way to do this?