Short: Useful coloured string efuns
From:  "Alexander Weidt et al." <tubmud@cs.tu-berlin.de>
Date: Fri Feb 23 13:42:33 2001
Type: Feature
New: Acknowledged
See also: f-990309-1

Hi Lars,,

hier schick ich Dir mal die Klasse basic/coloured_string.c.

Anmerkungen:
- #define REGEXP_TERMINAL_COLOUR_TOKEN "%\\^([^%]|(%%*[^%^]))*%%*\\^"
- LIB_CNTL_SEQUENCES->get_plain_mapping() liefert ein Mapping fuer
  terminal_colour(), welches alle hier im Mud definierten tokens enthaelt
  und auf "" abbildet.

Viele Gruesse,

  Coogan.
-----------------------------------------------------------------------
/*
 * /basic/coloured_string.c by Alfe for TubMud 01-Feb-14
 *
 * This is a basic class for handling coloured strings.
 */

#pragma strong_types

#include <regexps.h>
#include <libs.h>  // Coogan, 16-Feb-01

private nosave mapping plain_mapping;

/*
 * this applies `changer' on all parts of the given coloured_string
 * which are not a colour token and returns the result.
 */
string apply_on_coloured_string(string coloured_string,closure changer) {
  mixed h;
  int i;
  h = regexplode(coloured_string,REGEXP_TERMINAL_COLOUR_TOKEN);
  for (i=0; i<sizeof(h); i+=2)  // pick the non-tokens
    h[i] = funcall(changer,h[i]);
  return implode(h,"");
}

/*
 * this removes all colour tokens from the given coloured_string and
 * returns the result; thus it produces a verbatim string.
 */
string to_verbatim(string coloured_string) {
  if (!plain_mapping)
    plain_mapping = LIB_CNTL_SEQUENCES->get_plain_mapping();
  return terminal_colour(coloured_string,plain_mapping);
}

/*
 * this returns the visible string length of a coloured string
 */
int strlen_visible(string coloured_string) {
  if (!plain_mapping)
    plain_mapping = LIB_CNTL_SEQUENCES->get_plain_mapping();
  return strlen(terminal_colour(coloured_string,plain_mapping));
}

private void find_pos(string *parts,
                      int pos,status pos_from_back,
                      int real_part,int real_pos,
                      status as_start) {
  if (pos_from_back) {
    if (pos < 0) {
      real_part = sizeof(parts) - 1;
      real_pos = strlen(parts[<1]);
    } else {
      pos = strlen_visible(implode(parts,"")) - pos;
      find_pos(parts,pos,0,&real_part,&real_pos,as_start);
    }
  } else {  // from front
    // skip all parts which are too short:
    for (real_part = real_pos = 0;
         real_part < sizeof(parts) && (as_start?
                                       pos > strlen(parts[real_part]) :
                                       pos >= strlen(parts[real_part]));
         real_pos += strlen(parts[real_part]),
         pos -= strlen(parts[real_part]),
         real_part += 2)
      ;
    if (real_part >= sizeof(parts)) {  // too long?
      real_part = sizeof(parts) - 1;
      real_pos = strlen(parts[<1]);
    } else
      real_pos = pos;
  }
}

/*
 * this returns a substring of the given coloured string;
 */
varargs string substring(string coloured_string,
                         int start,             int stop,
                         status start_from_back,status stop_from_back) {
  string *parts;
  int *real_pos, *vis_pos;
  int i;
  int real_start_pos,real_stop_pos;
  int real_start_part,real_stop_part;
  parts = regexplode(coloured_string,REGEXP_TERMINAL_COLOUR_TOKEN);
  find_pos(parts,start,start_from_back,&real_start_part,&real_start_pos,1);
  find_pos(parts, stop, stop_from_back, &real_stop_part, &real_stop_pos,0);
  if (real_start_part > real_stop_part)
    return "";
  if (real_start_part == real_stop_part)
    parts[real_start_part] =
      parts[real_start_part][real_start_pos..real_stop_pos];
  else {
    parts[real_start_part] = parts[real_start_part][real_start_pos..];
    parts[real_stop_part]  = parts[real_stop_part][..real_stop_pos];
  }
  return implode(parts[real_start_part..real_stop_part],"");
}

varargs string coloured_sprintf(string format,varargs mixed *args) {
  int i;
  int strlen_diff;
  for (i=0; i<sizeof(args); i+=2)
    if (stringp(args[i+1])) {
      strlen_diff = strlen(args[i+1]) - strlen_visible(args[i+1]);
      if (args[i] < 0)
        args[i] -= strlen_diff;
      else
        args[i] += strlen_diff;
    }
  return apply(#'sprintf,format,args);
}

----------------------------------------------------------------------------
And Slava wrote in Oct 2001:

Recently I revealed great demand in something like hibrid of
sprintf+terminal_colour. Also I've discovered, that some time ago
there was something about this topic - f-990309-1.

In the matter of fact there is a great demand in such a functionality
in the driver. Till now, there are no official way, how to handle
coloured strings. I mean, yes you can do something with it - wrap,
and do very rudimentary formatting, using terminal_colour. But this is
not enough.

Furthermore, without additional and expensive workarounds, there are no
way how to:

1. format coloured string like in sprintf(), especially using such a
   modifiers, like: '-=', '|=', etc.
2. measure coloured string length like in strlen() or sizeof()
3. Use all the great funs, that we have for their non-coloured
   equivalents. All because of there is difference in actual string
   size and its displayable one.

