/* ============================================================================
* Copyright (C) 2015, Martial Bornet
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Author : Martial BORNET (MB) - 3rd of January, 2015
*
* Description : String colorization
*
* File : cr_main.c
*
* @(#) [MB] cr_main.c Version 1.69 du 16/01/23 -
*
* Sources from the original hl command are available on :
* https://github.com/mbornet-hl/hl
*
* Functions in this file :
* ~~~~~~~~~~~~~~~~~~~~~~~~
* - cr_list2argv
* - cr_lists2argv
* - cr_read_config_file
* - cr_read_config_files
* - cr_new_config
* - cr_new_arg
* - cr_new_args
* - cr_new_ptrs
* - cr_new_re_desc
* - cr_needs_arg
* - cr_get_config
* - cr_dump_ptrs
* - cr_dump_args
* - cr_getopt
* - cr_set_args
* - cr_add_to_list
* - cr_clear_marker_flags
* - cr_add_regexp
* - main
* - cr_usage
* - cr_display_args_list
* - cr_display_args
* - cr_display_config
* - cr_init_list
* - cr_add_config
* - cr_add_arg
* - cr_free_re
* - cr_marker2color
* - cr_set_desc
* - cr_read_input
* - cr_start_color
* - cr_end_color
* - cr_init_desc
* - cr_same_colors
* - cr_disp_line
* ============================================================================
*/
#include
#include
#include
#include
#include
#include
#include "cr_epri.h"
#define X if (G.debug) fprintf(stderr, "%s(%d)\n", __FILE__, __LINE__);
#define SC if (G.debug) fprintf(stderr, "==> %s(%d) Start color : [%c]\n", __FILE__, __LINE__, _c);
#define NC if (G.debug) fprintf(stderr, "==> %s(%d) No color : [%c]\n", __FILE__, __LINE__, _c);
#define EC if (G.debug) fprintf(stderr, "==> %s(%d) End color\n", __FILE__, __LINE__);
/******************************************************************************
CR_LIST2ARGV
******************************************************************************/
void cr_list2argv(struct cr_config *config)
{
char **_argv;
struct cr_arg *_arg, *_old_arg;
int _size;
_size = sizeof(char **) * (config->argc + 2);
config->argv = (char **) malloc(_size);
if (config->argv == 0) {
fprintf(stderr, cr_err_malloc, G.prgname);
exit(1);
}
bzero(config->argv, _size);
for (_argv = config->argv + 1, _arg = config->extract; _arg != 0; ) {
*_argv++ = _arg->value;
_old_arg = _arg;
_arg = _arg->next;
free(_old_arg);
}
*_argv = 0;
config->extract =
config->insert = 0;
}
/******************************************************************************
CR_LISTS2ARGV
******************************************************************************/
void cr_lists2argv(struct cr_configs *configs)
{
struct cr_config *_config;
for (_config = configs->extract; _config != 0; _config = _config->next) {
cr_list2argv(_config);
}
}
/******************************************************************************
CR_READ_CONFIG_FILE
******************************************************************************/
void cr_read_config_file(char *cfg_file)
{
if (access(cfg_file, 0) != 0) {
#if 0
fprintf(stderr, "%s: config file \"%s\" does not exist !\n",
G.prgname, cfg_file);
exit(1);
#else
return;
#endif
}
if ((yyin = fopen(cfg_file, "r")) == NULL) {
fprintf(stderr, "%s: cannot open \"%s\" !\n",
G.prgname, cfg_file);
perror("fopen");
exit(1);
}
yylex();
// cr_lists2argv(&G.configs);
}
/******************************************************************************
CR_READ_CONFIG_FILES
******************************************************************************/
void cr_read_config_files(void)
{
int _size;
char *_home, *_cfg_file;
if (G.config_file_read) {
return;
}
if ((_home = getenv("HOME")) == 0) {
fprintf(stderr, "%s: HOME variable undefined !\n", G.prgname);
exit(1);
}
_size = strlen(_home) + 1 + sizeof(CR_CONFIG_FILENAME);
if ((_cfg_file = malloc(_size)) == NULL) {
fprintf(stderr, cr_err_malloc, G.prgname);
exit(1);
}
sprintf(_cfg_file, "%s/%s", _home, CR_CONFIG_FILENAME);
cr_read_config_file(_cfg_file);
cr_read_config_file(CR_DEFLT_CONFIG_FILE);
cr_lists2argv(&G.configs);
G.config_file_read = TRUE;
}
/******************************************************************************
CR_NEW_CONFIG
******************************************************************************/
CR_NEW(config)
/******************************************************************************
CR_NEW_ARG
******************************************************************************/
CR_NEW(arg)
/******************************************************************************
CR_NEW_ARGS
******************************************************************************/
CR_NEW(args)
/******************************************************************************
CR_NEW_PTRS
******************************************************************************/
CR_NEW(ptrs)
/******************************************************************************
CR_NEW_RE_DESC
******************************************************************************/
CR_NEW(re_desc)
/******************************************************************************
CR_NEEDS_ARG
******************************************************************************/
bool cr_needs_arg(char opt, struct cr_args *args)
{
int _i;
for (_i = 0; args->opts[_i] != 0; _i++) {
if (args->opts[_i] != opt) continue;
if (args->opts[_i + 1] == ':') {
return TRUE;
}
else {
break;
}
}
return FALSE;
}
/******************************************************************************
CR_GET_CONFIG
******************************************************************************/
struct cr_config *cr_get_config(char *config_name, struct cr_args *args)
{
struct cr_config *_config;
if (!G.config_file_read) {
cr_read_config_files();
}
for (_config = args->configs->extract; _config != 0;
_config = _config->next) {
if (!strcmp(config_name, _config->name)) {
break;
}
}
return _config;
}
/******************************************************************************
CR_DUMP_PTRS
******************************************************************************/
void cr_dump_ptrs(struct cr_ptrs *ptrs)
{
printf("PTRS :\n");
printf(" curr_arg = \"%s\"\n", ptrs->curr_arg);
printf(" curr_idx = %d\n", ptrs->curr_idx);
printf(" next_arg = \"%s\"\n", ptrs->next_arg);
printf(" prev = %p\n", ptrs->prev);
printf(" config = %p\n", ptrs->config);
}
/******************************************************************************
CR_DUMP_ARGS
******************************************************************************/
void cr_dump_args(struct cr_args *args)
{
printf("ARGS :\n");
printf(" opts = \"%s\"\n", args->opts);
printf(" optarg = \"%s\"\n", args->optarg);
printf(" curr_ptrs = %p\n", args->curr_ptrs);
cr_dump_ptrs(args->curr_ptrs);
}
/******************************************************************************
CR_GETOPT
******************************************************************************/
int cr_getopt(struct cr_args *args)
{
/* Pointers always point to the arg or option to treat
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
char _c, *_arg, *_config_name, _next_char;
struct cr_ptrs *_ptrs, *_new_ptrs;
struct cr_config *_config;
for ( ; ; ) {
if (!(_ptrs = args->curr_ptrs)) {
/* No more argument to treat
~~~~~~~~~~~~~~~~~~~~~~~~~ */
CR_DEBUG("NO MORE ARGS.\n");
return -1;
}
if (*(_ptrs->curr_argv) == 0) {
/* No more argument for this level
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
CR_DEBUG("No more args for this level\n");
args->curr_ptrs = _ptrs->prev;
free(_ptrs);
continue;
}
CR_DEBUG("Current arg = %p \"%s\" idx = %d\n",
_ptrs->curr_arg, _ptrs->curr_arg, _ptrs->curr_idx);
if (_ptrs->curr_idx == 0) {
/* Treatment of a new argument
~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if (_ptrs->curr_arg[0] != '-') {
fprintf(stderr, "%s: argument with no associated option (\"%s\") ! \n",
G.prgname, _ptrs->curr_arg);
cr_dump_args(args);
exit(1);
}
else {
/* Continuation of the treatment of an argument
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
_ptrs->curr_idx++;
if (_ptrs->curr_arg[1] != '-') {
/* Continuation of the one letter options
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
break;
}
else {
/* Configuration name
~~~~~~~~~~~~~~~~~~ */
_config_name = _ptrs->curr_arg + 2;
CR_DEBUG("==> CONFIG : \"%s\"\n", _config_name);
_ptrs->curr_argv++;
_ptrs->curr_idx = 0;
_ptrs->curr_arg = *_ptrs->curr_argv;
CR_DEBUG("Apres incrementation pointeur CONFIG :\n");
CR_DEBUG("Current arg = %p \"%s\"\n", _ptrs->curr_arg, _ptrs->curr_arg);
_config = cr_get_config(_config_name, args);
if (_config == 0) {
fprintf(stderr, "%s: undefined configuration (%s) !\n",
G.prgname, _config_name);
exit(1);
}
/* Detection of recursive loop
~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if (_config->visited) {
fprintf(stderr, "%s: configuration loop for \"%s\" !\n",
G.prgname, _config->name);
exit(1);
}
_config->visited = TRUE;
_new_ptrs = cr_new_ptrs();
_new_ptrs->prev = args->curr_ptrs;
_new_ptrs->argc = _config->argc;
_new_ptrs->argv = _config->argv;
_new_ptrs->curr_argv = _new_ptrs->argv + 1;
_new_ptrs->curr_arg = _new_ptrs->argv[1];
_new_ptrs->config = _config; // Usefullness ???
args->curr_ptrs = _new_ptrs;
continue;
}
}
}
else {
break;
}
}
/* Options
~~~~~~~ */
_c = _ptrs->curr_arg[_ptrs->curr_idx];
CR_DEBUG("==> OPTION : '%c'\n", _c);
_ptrs->curr_idx++;
_next_char = _ptrs->curr_arg[_ptrs->curr_idx];
if (cr_needs_arg(_c, args)) {
CR_DEBUG(" (argument needed)\n");
_arg = *(_ptrs->curr_argv + 1);
if (_arg == 0 || _arg[0] == '-' || _next_char != 0) {
fprintf(stderr, "%s: missing argument for \"-%c\" !\n",
G.prgname, _c);
cr_dump_args(args);
exit(1);
}
args->optarg = _arg;
CR_DEBUG(" OPTARG = \"%s\"\n", args->optarg);
_ptrs->curr_argv++;
_ptrs->curr_arg = *_ptrs->curr_argv;
}
else {
CR_DEBUG(" no argument needed.\n");
args->optarg = 0;
}
if (_next_char == 0) {
CR_DEBUG(" No more 1 letter option\n");
CR_DEBUG(" Current arg = %p \"%s\"\n", _ptrs->curr_arg, _ptrs->curr_arg);
if (*(_ptrs->curr_argv + 1) == 0) {
/* No more argument for this level
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
CR_DEBUG("No more args for this level\n");
args->curr_ptrs = _ptrs->prev;
free(_ptrs);
}
else {
_ptrs->curr_argv++;
_ptrs->curr_arg = *_ptrs->curr_argv;
_ptrs->curr_idx = 0;
CR_DEBUG(" Next arg = %p \"%s\"\n", _ptrs->curr_arg, _ptrs->curr_arg);
}
}
return _c;
}
/******************************************************************************
CR_SET_ARGS
******************************************************************************/
struct cr_args *cr_set_args(int argc, char **argv, char *opts,
struct cr_configs *configs)
{
struct cr_args *_args;
struct cr_ptrs *_ptrs;
_args = cr_new_args();
_ptrs = cr_new_ptrs();
_ptrs->argc = argc;
_ptrs->argv = argv + 1;
_ptrs->curr_argv = _ptrs->argv;
_ptrs->curr_arg = argv[1];
_ptrs->curr_idx = 0;
_ptrs->next_arg = NULL;
_ptrs->prev = NULL;
_ptrs->config = NULL;
_args->opts = opts;
_args->optarg = NULL;
_args->curr_ptrs = _ptrs;
_args->configs = configs;
return _args;
}
/******************************************************************************
CR_ADD_TO_LIST
******************************************************************************/
void cr_add_to_list(struct cr_re_desc *re)
{
if (G.extract_RE == 0) {
G.extract_RE =
G.insert_RE = re;
}
else {
G.insert_RE->next = re;
G.insert_RE = re;
}
}
/******************************************************************************
CR_CLEAR_MARKER_FLAGS
******************************************************************************/
inline void cr_clear_marker_flags(void)
{
G.begin_specified = FALSE;
G.end_specified = FALSE;
}
/******************************************************************************
CR_ADD_REGEXP
******************************************************************************/
void cr_add_regexp(int color, char *regexp)
{
struct cr_re_desc *_re;
int _error;
char _errbuf[256], *_p;
if (!G.end_specified) {
_re = cr_new_re_desc();
_re->regex[0] = regexp;
_re->cflags = G.cflags;
_re->col.col_num = color;
_re->col.intensity = G.intensity;
_re->col.out = G.out;
_re->max_sub = 1;
G.out = stdout;
/* Count number of possible sub strings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
for (_p = regexp; (*_p); _p++) {
if (*_p == '(') {
_re->max_sub++;
}
}
if ((_error = regcomp(&_re->reg[0], regexp, _re->cflags)) != 0) {
(void) regerror(_error, &_re->reg[0], _errbuf, sizeof(_errbuf));
fprintf(stderr, "%s: regcomp error for \"%s\" : %s\n",
G.prgname, regexp, _errbuf);
exit(1);
}
cr_add_to_list(_re);
G.last_RE = _re;
G.last_color = color;
}
else {
_re = G.last_RE;
_re->regex[1] = regexp;
if (_re->regex[1]) {
if (!strcmp(_re->regex[0], _re->regex[1])) {
_re->begin_is_end = TRUE;
}
if (regcomp(&_re->reg[1], regexp, _re->cflags) != 0) {
fprintf(stderr, "%s: regcomp error for \"%s\" !\n", G.prgname, regexp);
exit(1);
}
}
cr_clear_marker_flags();
G.last_RE = 0;
G.last_color = 0;
}
}
/******************************************************************************
MAIN
******************************************************************************/
int main(int argc, char *argv[])
{
int _opt, _i, _lg, _argc;
struct cr_args *_args;
struct cr_re_desc *_re;
char *_env_var_name, *_env_deflt, *_deflt_color_opt,
**_argv, *_argv_deflt[4];
G.prgname = argv[0];
G.out = stdout;
switch (argc) {
case 1:
cr_usage(FALSE);
break;
case 2:
if (argv[1][0] != '-') {
/* Only one argument (the regular expression)
* => get the default color in the environment variable
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
_env_var_name = CR_ENV_DEFLT;
if ((_env_deflt = getenv(_env_var_name)) == 0) {
#if 0
fprintf(stderr, "%s: minimal syntax but \"%s\" is undefined !\n",
G.prgname, _env_var_name);
exit(1);
#else
_env_deflt = CR_DEFLT_COLOR;
#endif
}
_lg = strlen(_env_deflt);
if ((_deflt_color_opt = malloc(_lg + 2)) == 0) {
fprintf(stderr, cr_err_malloc, G.prgname);
exit(1);
}
sprintf(_deflt_color_opt, "-%s", _env_deflt);
_argv_deflt[0] = argv[0];
_argv_deflt[1] = _deflt_color_opt;
_argv_deflt[2] = argv[1];
_argv_deflt[3] = NULL;
_argc = 3;
_argv = _argv_deflt;
}
else {
_argc = argc;
_argv = argv;
}
break;
default:
_argc = argc;
_argv = argv;
break;
}
cr_init_list();
G.intensity = CR_DEFLT_INTENSITY;
cr_clear_marker_flags();
/* Decoding of arguments
~~~~~~~~~~~~~~~~~~~~~ */
_args = cr_set_args(_argc, _argv,
"hHuVvEr:g:y:b:m:c:w:R:G:Y:B:M:C:W:n:DLdei1234%.:",
&G.configs);
while ((_opt = cr_getopt(_args)) != -1) {
switch (_opt) {
case 'h':
cr_usage(FALSE);
break;
case 'H':
cr_read_config_files();
cr_usage(TRUE);
break;
case 'E':
G.out = stderr;
break;
case 'd' :
G.debug = TRUE;
break;
case 'D' :
G.disp_regex = TRUE;
break;
case 'L' :
G.disp_lex = TRUE;
break;
case 'r':
cr_add_regexp(CR_RED, _args->optarg);
break;
case 'g':
cr_add_regexp(CR_GREEN, _args->optarg);
break;
case 'y':
cr_add_regexp(CR_YELLOW, _args->optarg);
break;
case 'b':
cr_add_regexp(CR_BLUE, _args->optarg);
break;
case 'm':
cr_add_regexp(CR_MAGENTA, _args->optarg);
break;
case 'c':
cr_add_regexp(CR_CYAN, _args->optarg);
break;
case 'w':
cr_add_regexp(CR_WHITE, _args->optarg);
break;
case 'R':
cr_add_regexp(CR_RED_REV, _args->optarg);
break;
case 'G':
cr_add_regexp(CR_GREEN_REV, _args->optarg);
break;
case 'Y':
cr_add_regexp(CR_YELLOW_REV, _args->optarg);
break;
case 'B':
cr_add_regexp(CR_BLUE_REV, _args->optarg);
break;
case 'M':
cr_add_regexp(CR_MAGENTA_REV, _args->optarg);
break;
case 'C':
cr_add_regexp(CR_CYAN_REV, _args->optarg);
break;
case 'W':
cr_add_regexp(CR_WHITE_REV, _args->optarg);
break;
case 'n':
cr_add_regexp(CR_NO_COLOR, _args->optarg);
break;
case 'e':
G.cflags |= REG_EXTENDED;
break;
case 'i':
G.cflags |= REG_ICASE;
break;
case 'u':
setvbuf(stdout, (char *) 0, _IONBF, 0);
break;
case 'v':
G.verbose = TRUE;
break;
case 'V':
fprintf(stderr, "%s: version %s\n", G.prgname, "1.69");
exit(1);
break;
case '1':
case '2':
case '3':
case '4':
G.intensity = _opt - '0';
break;
case '%':
/* Begin marker
~~~~~~~~~~~~ */
if (G.begin_specified) {
fprintf(stderr, "%s: begin marker without end marker !\n",
G.prgname);
exit(1);
}
G.begin_specified = TRUE;
break;
case '.':
/* End marker
~~~~~~~~~~ */
if (!G.begin_specified) {
fprintf(stderr, "%s: end marker without begin marker !\n",
G.prgname);
exit(1);
}
G.end_specified = TRUE;
cr_add_regexp(G.last_color, _args->optarg);
break;
default:
fprintf(stderr, "%s: unknown option '%c' !\n", G.prgname, _opt);
cr_usage(FALSE);
break;
}
}
if (G.disp_regex) {
for (_i = 0, _re = G.extract_RE; _re != NULL; _re = _re->next) {
printf("[%2d] ", ++_i);
cr_start_color(&_re->col);
printf("%s", _re->regex[0]);
cr_end_color(&_re->col);
printf("\n");
if (_re->regex[1]) {
printf(" => ");
cr_start_color(&_re->col);
printf("%s", _re->regex[1]);
cr_end_color(&_re->col);
printf("\n");
}
}
}
cr_read_input();
cr_free_RE();
return 0;
}
/******************************************************************************
CR_USAGE
******************************************************************************/
void cr_usage(bool disp_config)
{
fprintf(stderr, "%s: version %s\n", G.prgname, "1.69");
fprintf(stderr, "Usage: %s [-h|-H|-V|-[[%%.]eiuvdDEL1234][-[rgybmcwRGYBMCWn] regexp ...][--config_name ...] ]\n",
G.prgname);
fprintf(stderr, " -h : help\n");
fprintf(stderr, " -H : help + configuration names\n");
fprintf(stderr, " -V : version\n");
fprintf(stderr, " -v : verbose\n");
fprintf(stderr, " -u : do not bufferize output on stdout\n");
fprintf(stderr, " -e : extended regular expressions\n");
fprintf(stderr, " -i : ignore case\n");
fprintf(stderr, " -E : print on stderr\n");
fprintf(stderr, " -r : red\n");
fprintf(stderr, " -g : green\n");
fprintf(stderr, " -y : yellow\n");
fprintf(stderr, " -b : blue\n");
fprintf(stderr, " -m : magenta\n");
fprintf(stderr, " -c : cyan\n");
fprintf(stderr, " -w : white\n");
fprintf(stderr, " -R : red (reverse video)\n");
fprintf(stderr, " -G : green (reverse video)\n");
fprintf(stderr, " -Y : yellow (reverse video)\n");
fprintf(stderr, " -B : blue (reverse video)\n");
fprintf(stderr, " -M : magenta (reverse video)\n");
fprintf(stderr, " -C : cyan (reverse video)\n");
fprintf(stderr, " -W : white (reverse video)\n");
fprintf(stderr, " -n : never colorize\n");
fprintf(stderr, " -%%c : specifies the beginning of a range colorized in color 'c'\n");
fprintf(stderr, " -. : specifies the end of the previous range\n");
fprintf(stderr, " -d : debug\n");
fprintf(stderr, " -D : display regular expressions\n");
fprintf(stderr, " -L : lex debug\n");
fprintf(stderr, " -1 : color brightness (half-bright)\n");
fprintf(stderr, " -2 : color brightness (normal : default)\n");
fprintf(stderr, " -3 : color brightness (bright)\n");
fprintf(stderr, " -4 : color brightness (underscore)\n");
if (disp_config) {
cr_display_config();
}
exit(1);
}
/******************************************************************************
CR_DISPLAY_ARGS_LIST
******************************************************************************/
void cr_display_args_list(struct cr_config *config)
{
struct cr_arg *_arg;
for (_arg = config->extract; _arg != 0; _arg = _arg->next) {
fprintf(stderr, " %s\n", _arg->value);
}
fprintf(stderr, "\n");
}
/******************************************************************************
CR_DISPLAY_ARGS
******************************************************************************/
void cr_display_args(struct cr_config *config)
{
char **_argv;
for (_argv = config->argv + 1; *_argv != 0; _argv++) {
fprintf(stderr, " %s\n", *_argv);
}
fprintf(stderr, "\n");
}
/******************************************************************************
CR_DISPLAY_CONFIG
******************************************************************************/
void cr_display_config(void)
{
struct cr_config *_config;
fprintf(stderr, " Configurations :\n");
for (_config = G.configs.extract; _config != 0; _config = _config->next) {
fprintf(stderr, " --%s\n", _config->name);
if (G.verbose) {
cr_display_args(_config);
}
}
}
/******************************************************************************
CR_INIT_LIST
******************************************************************************/
void cr_init_list(void)
{
int _i;
for (_i = 0; _i < CR_NB_COLORS; _i++) {
G.list[_i] = -1;
}
G.idx_list = 0;
}
/******************************************************************************
CR_ADD_CONFIG
******************************************************************************/
void cr_add_config(struct cr_config *config)
{
if (G.configs.insert == 0) {
G.configs.insert =
G.configs.extract = config;
}
else {
G.configs.insert->next = config;
G.configs.insert = config;
}
}
/******************************************************************************
CR_ADD_ARG
******************************************************************************/
void cr_add_arg(struct cr_arg *arg)
{
struct cr_config *_config;
assert(G.configs.insert != 0);
_config = G.configs.insert;
if(_config->insert == 0) {
_config->insert =
_config->extract = arg;
}
else {
_config->insert->next = arg;
_config->insert = arg;
}
_config->argc++;
}
/******************************************************************************
CR_FREE_RE
******************************************************************************/
void cr_free_RE(void)
{
struct cr_re_desc *_re;
for (_re = G.extract_RE; _re != NULL; _re = _re->next) {
regfree(&_re->reg[0]);
}
}
/******************************************************************************
CR_MARKER2COLOR
******************************************************************************/
void cr_marker2color( struct cr_re_desc *re)
{
int _i = 0, _curr_level;
struct cr_col_desc *_desc;
_curr_level = re->curr_level;
//fprintf(stderr, "[%2d] >>> curr_level : %d\n", _i, _curr_level);
for (_i = 0, _desc = G.desc; _i < G.length; _i++, _desc++) {
switch (_desc->marker) {
case 1:
_curr_level++;
//fprintf(stderr, "[%2d] +++ curr_level : %d\n", _i, _curr_level);
break;
case -1:
if (_curr_level > 0) {
_curr_level--;
}
//fprintf(stderr, "[%2d] --- curr_level : %d\n", _i, _curr_level);
break;
case 0:
//fprintf(stderr, "[%2d] === curr_level : %d\n", _i, _curr_level);
break;
default:
fprintf(stderr, "%s: internal error\n", G.prgname);
exit(1);
break;
}
if (!_desc->used) {
if (_curr_level > 0) {
// fprintf(stderr, "==> [%2d] [%c] Color set. Col num = %d\n",
// _i, G.line[_i], re->col.col_num);
_desc->used = TRUE;
_desc->col = &re->col;
}
else {
// fprintf(stderr, "==> [%2d] [%c] NO COLOR.\n", _i, G.line[_i]);
}
}
else {
// fprintf(stderr, "==> [%2d] [%c] Already colorized.\n", _i, G.line[_i]);
}
}
re->curr_level = _curr_level;
//fprintf(stderr, "[%2d] <<< curr_level : %d\n", _i, _curr_level);
}
/******************************************************************************
CR_SET_DESC
******************************************************************************/
void cr_set_desc(struct cr_re_desc *re, int offset, int s, int e, int marker)
{
int _i;
struct cr_col_desc *_desc;
//fprintf(stderr, "SET_DESC : s = %3d e = %3d marker = %d\n", s, e, marker);
if (re->regex[1] == 0) {
/* RE descriptor does not define a range
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
for (_i = s, _desc = &G.desc[offset + s]; _i <= e; _i++, _desc++) {
if (!_desc->used) {
_desc->used = TRUE;
_desc->col = &re->col;
}
}
}
else {
switch (marker) {
case 1:
_desc = &G.desc[offset + s];
break;
case -1:
_desc = &G.desc[offset + e + 1]; // XXX : ATTENTION AU SEGV
break;
default:
fprintf(stderr, "%s: erreur interne, marker = %d\n",
G.prgname, marker);
exit(1);
}
_desc->marker = marker;
}
}
/******************************************************************************
CR_READ_INPUT
******************************************************************************/
void cr_read_input(void)
{
int _i, _j, _n, _s = 0, _e = 0, _off, _idx_last,
_marker;
struct cr_re_desc *_re;
size_t _nmatch;
regmatch_t _pmatch[CR_SIZE + 1];
int _eflags = 0;
char _debug_str[CR_SIZE + 1];
_nmatch = sizeof(_pmatch) / sizeof(_pmatch[0]);
for (_n = sizeof(G.line); fgets(G.line, _n, stdin) != 0; _n = sizeof(G.line)) {
/* Reset of color descriptors
~~~~~~~~~~~~~~~~~~~~~~~~~~ */
cr_init_desc();
G.length = strlen(G.line);
_idx_last = G.length - 1;
if (G.line[_idx_last] == '\n') {
G.line[_idx_last] = 0;
G.newline = TRUE;
}
else {
G.newline = FALSE;
}
if (G.debug) {
fprintf(stderr, "LENGTH : %4d\n", G.length);
fprintf(stderr, "LINE : [%s] :\n", G.line);
}
/* Loop on regexp
~~~~~~~~~~~~~~ */
for (_re = G.extract_RE; _re != NULL; _re = _re->next) {
/* Loop on BEGIN / END regexp
~~~~~~~~~~~~~~~~~~~~~~~~~~ */
for (_i = 0; _i < 2; _i++) {
if (_re->regex[_i]) {
if (_i == 1 && _re->begin_is_end) {
break;
}
/* Search for multiple matches
~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
for (_off = 0, _eflags = 0;
_off < G.length &&
regexec(&_re->reg[_i], G.line + _off, _nmatch, _pmatch,
_eflags) == 0; _off += _e + 1, _eflags = REG_NOTBOL) {
if (G.debug) {
fprintf(stderr, " Match for [%s] // [%s]\n",
G.line + _off, _re->regex[_i]);
fprintf(stderr, " LINE : [%s] :\n", G.line + _off);
}
/* Loop on substrings
~~~~~~~~~~~~~~~~~~ */
for (_j = 0; _j < _re->max_sub; _j++) {
if (_j == 0 && _pmatch[1].rm_so != -1) {
continue;
}
_s = _pmatch[_j].rm_so;
_e = _pmatch[_j].rm_eo - 1;
if (G.debug) {
strncpy(_debug_str,
G.line + _off + _s, _e - _s + 1);
_debug_str[_e -_s + 1] = 0;
fprintf(stderr,
" OFFSET = %3d : %3d => %3d [%s] [%s]\n",
_off, _s, _e, _re->regex[_i], _debug_str);
}
if (!_re->begin_is_end) {
if (_i == 0) _marker = 1;
else _marker = -1;
}
else {
_re->inside_zone = !_re->inside_zone;
if (_re->inside_zone) _marker = 1;
else _marker = -1;
}
if (_s >= 0) {
cr_set_desc(_re, _off, _s, _e, _marker);
}
if (G.debug) {
fprintf(stderr, "cr_set_desc : %d, %d => %d, %d\n",
_off, _s, _e, _marker);
fprintf(stderr, "\n");
}
}
/* To handle empty strings
~~~~~~~~~~~~~~~~~~~~~~~ */
if (_e < 0) {
_e = 0;
}
}
if (G.debug) {
fprintf(stderr, " NO MATCH for [%s] // [%s]\n",
G.line + _off, _re->regex[_i]);
}
}
if (G.debug) {
fprintf(stderr, "\n");
}
}
if (_re->regex[1]) {
cr_marker2color(_re);
}
if (G.debug) {
fprintf(stderr, "\n");
}
}
cr_disp_line();
}
}
/******************************************************************************
CR_START_COLOR
******************************************************************************/
void cr_start_color(struct cr_color *col)
{
int _col_num;
FILE *_out;
if (col) _out = col->out;
else _out = stdout;
_col_num = col->col_num;
if (_col_num > CR_WHITE) {
if (_col_num != CR_NO_COLOR) {
/* Reverse video
~~~~~~~~~~~~~ */
switch (col->intensity) {
case 1:
fprintf(_out, "\033[%sm", cr_best_fg[_col_num - 9][0]);
fprintf(_out, "\033[48;5;%dm", cr_col_codes[_col_num - 9][0]);
break;
case 2:
fprintf(_out, "\033[%sm", cr_best_fg[_col_num - 9][1]);
fprintf(_out, "\033[48;5;%dm", cr_col_codes[_col_num - 9][1]);
break;
case 3:
fprintf(_out, "\033[%sm", cr_best_fg[_col_num - 9][2]);
fprintf(_out, "\033[48;5;%dm", cr_col_codes[_col_num - 9][2]);
break;
case 4:
fprintf(_out, "\033[07;04;%dm", 30 + _col_num - 8);
break;
default:
fprintf(stderr, "%s: invalid color brightness !\n", G.prgname);
exit(1);
}
}
else {
/* No color
~~~~~~~~ */
}
}
else {
/* Normal video
~~~~~~~~~~~~ */
switch (col->intensity) {
case 1:
fprintf(_out, "\033[02;%dm", 30 + _col_num); // Half-bright
break;
case 2:
fprintf(_out, "\033[%dm", 30 + _col_num); // Normal
break;
case 3:
fprintf(_out, "\033[01;%dm", 30 + _col_num); // Bold
break;
case 4:
fprintf(_out, "\033[04;%dm", 30 + _col_num); // Underscore
break;
default:
fprintf(stderr, "%s: invalid color brightness !\n", G.prgname);
exit(1);
}
}
}
/******************************************************************************
CR_END_COLOR
******************************************************************************/
void cr_end_color(struct cr_color *col)
{
FILE *_out;
if (col) _out = col->out;
else _out = stdout;
if (col && col->col_num != CR_NO_COLOR) {
fprintf(_out, "\033[0m");
}
}
/******************************************************************************
CR_INIT_DESC
******************************************************************************/
void cr_init_desc(void)
{
struct cr_col_desc *_desc;
G.length = 0;
for (_desc = G.desc; _desc < (&G.desc[sizeof(G.desc) / sizeof(G.desc[0])]);
_desc++) {
_desc->col = NULL;
_desc->used = FALSE;
_desc->marker = 0;
}
}
/******************************************************************************
CR_SAME_COLORS
******************************************************************************/
inline bool cr_same_colors(struct cr_color *col1, struct cr_color *col2)
{
return (col1->col_num == col2->col_num)
&& (col1->intensity == col2->intensity);
}
/******************************************************************************
CR_DISP_LINE
******************************************************************************/
void cr_disp_line(void)
{
int _i, _c;
struct cr_col_desc *_desc;
for (_i = 0, _desc = G.desc; _i < G.length; _i++, _desc++) {
_c = G.line[_i];
if (_c == '\n' || (_c == 0 && G.newline)) {
if (G.curr_col) {
cr_end_color(G.curr_col);
putc('\n', G.curr_col->out);
G.curr_col = NULL;
}
else {
putc('\n', stdout);
}
}
else if (_desc->used) {
/* Character is in color
~~~~~~~~~~~~~~~~~~~~~ */
if (G.curr_col == NULL) {
/* Previous character was not in color
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
cr_start_color(_desc->col);
putc(_c, _desc->col->out);
G.curr_col = _desc->col;
}
else {
/* Previous character was in color
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if (cr_same_colors(_desc-> col, G.curr_col)) {
/* No color change
~~~~~~~~~~~~~~~ */
putc(_c, G.curr_col->out);
}
else {
/* Color change
~~~~~~~~~~~~ */
cr_end_color(G.curr_col);
cr_start_color(_desc->col);
putc(_c, _desc->col->out);
G.curr_col = _desc->col;
}
}
}
else {
/* Character is not in color
~~~~~~~~~~~~~~~~~~~~~~~~~ */
if (G.curr_col) {
/* Previous character was in color
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
cr_end_color(G.curr_col);
putc(_c, G.curr_col->out);
G.curr_col = NULL;
}
else {
/* Previous character was not in color
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
putc(_c, stdout);
}
}
}
#if 0
if (G.newline) {
if (G.curr_col) {
cr_end_color(G.curr_col);
}
}
#endif
_desc--;
if (_desc->used) {
cr_end_color(G.curr_col);
}
G.curr_col = NULL;
}