1/* Handling of color output.
2   Copyright (C) 2011 Red Hat, Inc.
3   This file is part of Red Hat elfutils.
4   Written by Ulrich Drepper <drepper@redhat.com>, 2011.
5
6   Red Hat elfutils is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by the
8   Free Software Foundation; version 2 of the License.
9
10   Red Hat elfutils is distributed in the hope that it will be useful, but
11   WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   General Public License for more details.
14
15   You should have received a copy of the GNU General Public License along
16   with Red Hat elfutils; if not, write to the Free Software Foundation,
17   Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
18
19   Red Hat elfutils is an included package of the Open Invention Network.
20   An included package of the Open Invention Network is a package for which
21   Open Invention Network licensees cross-license their patents.  No patent
22   license is granted, either expressly or impliedly, by designation as an
23   included package.  Should you wish to participate in the Open Invention
24   Network licensing program, please visit www.openinventionnetwork.com
25   <http://www.openinventionnetwork.com>.  */
26
27#ifdef HAVE_CONFIG_H
28# include <config.h>
29#endif
30
31#include <argp.h>
32#include <error.h>
33#include <libintl.h>
34#include <stdlib.h>
35#include <string.h>
36#include <unistd.h>
37#include "system.h"
38
39
40/* Prototype for option handler.  */
41static error_t parse_opt (int key, char *arg, struct argp_state *state);
42
43/* Option values.  */
44#define OPT_COLOR 0x100100
45
46/* Definitions of arguments for argp functions.  */
47static const struct argp_option options[] =
48{
49  { "color", OPT_COLOR, "WHEN", OPTION_ARG_OPTIONAL,
50    N_("colorize the output.  WHEN defaults to 'always' or can be 'auto' or 'never'"), 0 },
51
52  { NULL, 0, NULL, 0, NULL, 0 }
53};
54
55/* Parser data structure.  */
56const struct argp color_argp =
57  {
58    options, parse_opt, NULL, NULL, NULL, NULL, NULL
59  };
60
61/* Coloring mode.  */
62enum color_enum color_mode;
63
64/* Colors to use for the various components.  */
65char *color_address = "";
66char *color_bytes = "";
67char *color_mnemonic = "";
68char *color_operand = NULL;
69char *color_operand1 = "";
70char *color_operand2 = "";
71char *color_operand3 = "";
72char *color_label = "";
73char *color_undef = "";
74char *color_undef_tls = "";
75char *color_undef_weak = "";
76char *color_symbol = "";
77char *color_tls = "";
78char *color_weak = "";
79
80const char color_off[] = "\e[0m";
81
82
83/* Handle program arguments.  */
84static error_t
85parse_opt (int key, char *arg,
86	   struct argp_state *state __attribute__ ((unused)))
87{
88  switch (key)
89    {
90    case OPT_COLOR:
91      if (arg == NULL)
92	color_mode = color_always;
93      else
94	{
95	  static const struct
96	  {
97	    const char str[7];
98	    enum color_enum mode;
99	  } values[] =
100	      {
101		{ "always", color_always },
102		{ "yes", color_always },
103		{ "force", color_always },
104		{ "never", color_never },
105		{ "no", color_never },
106		{ "none", color_never },
107		{ "auto", color_auto },
108		{ "tty", color_auto },
109		{ "if-tty", color_auto }
110	      };
111	  const int nvalues = sizeof (values) / sizeof (values[0]);
112	  int i;
113	  for (i = 0; i < nvalues; ++i)
114	    if (strcmp (arg, values[i].str) == 0)
115	      {
116		color_mode = values[i].mode;
117		if (color_mode == color_auto)
118		  color_mode
119		    = isatty (STDOUT_FILENO) ? color_always : color_never;
120		break;
121	      }
122	  if (i == nvalues)
123	    {
124	      error (0, 0, dgettext ("elfutils", "\
125%s: invalid argument '%s' for '--color'\n\
126valid arguments are:\n\
127  - 'always', 'yes', 'force'\n\
128  - 'never', 'no', 'none'\n\
129  - 'auto', 'tty', 'if-tty'\n"),
130		     program_invocation_short_name, arg);
131	      argp_help (&color_argp, stderr, ARGP_HELP_SEE,
132			 program_invocation_short_name);
133	      exit (EXIT_FAILURE);
134	    }
135	}
136
137      if (color_mode == color_always)
138	{
139	  const char *env = getenv ("ELFUTILS_COLORS");
140	  if (env != NULL)
141	    {
142	      do
143		{
144		  const char *start = env;
145		  while (*env != '=' && *env != '\0')
146		    ++env;
147		  if (*env == '=' && env != start)
148		    {
149		      size_t name_len = env - start;
150		      const char *val = ++env;
151		      env = strchrnul (env, ':');
152		      if (val != env)
153			{
154			  static const struct
155			  {
156			    unsigned char len;
157			    const char name[sizeof (char *) - 1];
158			    char **varp;
159			  } known[] =
160			      {
161#define E(name, var) { sizeof (#name) - 1, #name,  &color_##var }
162				E (a, address),
163				E (b, bytes),
164				E (m, mnemonic),
165				E (o, operand),
166				E (o1, operand1),
167				E (o1, operand2),
168				E (o1, operand3),
169				E (l, label),
170				E (u, undef),
171				E (ut, undef_tls),
172				E (uw, undef_weak),
173				E (sy, symbol),
174				E (st, tls),
175				E (sw, weak),
176			      };
177			  const size_t nknown = (sizeof (known)
178						 / sizeof (known[0]));
179
180			  for (size_t i = 0; i < nknown; ++i)
181			    if (name_len == known[i].len
182				&& memcmp (start, known[i].name, name_len) == 0)
183			      {
184				if (asprintf (known[i].varp, "\e[%.*sm",
185					      (int) (env - val), val) < 0)
186				  error (EXIT_FAILURE, errno,
187					 gettext ("cannot allocate memory"));
188				break;
189			      }
190			}
191		      if (*env == ':')
192			++env;
193		    }
194		}
195	      while (*env != '\0');
196
197	      if (color_operand != NULL)
198		{
199		  if (color_operand1[0] == '\0')
200		    color_operand1 = color_operand;
201		  if (color_operand2[0] == '\0')
202		    color_operand2 = color_operand;
203		  if (color_operand3[0] == '\0')
204		    color_operand3 = color_operand;
205		}
206	    }
207#if 0
208	  else
209	    {
210	      // XXX Just for testing.
211	      color_address = xstrdup ("\e[38;5;166;1m");
212	      color_bytes = xstrdup ("\e[38;5;141m");
213	      color_mnemonic = xstrdup ("\e[38;5;202;1m");
214	      color_operand1 = xstrdup ("\e[38;5;220m");
215	      color_operand2 = xstrdup ("\e[38;5;48m");
216	      color_operand3 = xstrdup ("\e[38;5;112m");
217	      color_label = xstrdup ("\e[38;5;21m");
218	    }
219#endif
220	}
221      break;
222
223    default:
224      return ARGP_ERR_UNKNOWN;
225    }
226  return 0;
227}
228