    Applying foreground color using backlighting

    This patch adds the ability to specify a foreground color for
    a backlighting class. The foreground color takes priority over syntax
    highlighting colors. This allows (in particular) space and tab characters
    displayed as glyphs to appear with a uniform color no matter in which
    highlighting pattern they appear. (It isn't very useful in other cases.)

    To specify both foreground and background in the backlightCharTypes
    resource, just separate them with a slash. (Note that this syntax should be
    changed, as well as that for background text styles, to allow new color
    specification formats, eg rgb:/e7/e7/c0.) If two colors are found, the first
    will be treated as foreground, the second as background.

    (We should really separate out the color allocation currently in
    highlighting.h.)

    2006-11-22

    Changes made to track CVS Head.

    2008-03-12

    Added the macro built-in set_backlight() which allows a simple interface for
    changing backlighting specifications.

diff -ur nedit_official/ nedit_mod/
diff -ur nedit_official/doc/help.etx nedit_mod/doc/help.etx
--- nedit_official/doc/help.etx	2008-02-19 23:31:51.000000000 +0100
+++ nedit_mod/doc/help.etx	2008-03-12 02:36:51.000000000 +0100
@@ -2503,10 +2503,11 @@
   The current wrap text mode. Values are "none", "auto" or "continuous".
 
 ..Disabled for 5.4 release.
-..**$backlight_string**
-..  The current value of the window's backlighting specification. This is empty
-..  if backlighting is turned off. It can be changed through calls to the
-..  built-in macro function set_backlight_string().
+  **$backlight_string**
+    The current value of the window's backlighting specification. This is empty
+    if backlighting is turned off. It can be changed through calls to the
+    built-in macro function set_backlight_string() or indirectly with
+    set_backlight().
 
 
 3>Built-in Subroutines
@@ -2681,14 +2682,33 @@
   and confined horizontally to characters displayed between positions "left",
   and "right".
 
-..Disabled for 5.4 release.
-..**set_backlight_string( [string] )**
-..  Applies the given string, which should be in the format of the
-..  nedit*backlightCharTypes X resource, to the current text window, turning on
-..  backlighting.  If the value of the string passed is "default", or if no
-..  parameter is passed, the nedit.backlightCharTypes X resource's own value will
-..  be used.  If the empty string, "", is passed, backlighting will be turned
-..  off.
+**set_backlight( character_range [, color1 [, color2 ]] )**
+  Adjusts the backlighting for the current window to color characters defined
+  by the range character_range.  If only one color (color1) is given, this is
+  used as a background color for these characters (if they have no syntax
+  highlighting or rangeset background color already applied).  If two colors
+  are given, both foreground (color1) and background (color2) are applied.  (The
+  foreground color takes precedence over syntax highlighting coloring.)  If
+  either color is "none" (or consists of spaces only), the coloring is applied
+  according to non-backlighted behavior; if empty, the appropriate backlighting
+  for the characters remains unchanged.  Otherwise, valid colors must be
+  supplied to avoid macro execution failure.
+    The character range is a string containing all characters for which the new
+  backlighting should be applied; two characters have special meaning: if "^"
+  is seen as the first character, the remaining specification defines characters
+  not to be affected. The character "-" between two others specifies a range
+  defined by the character set, including all characters whose order (code
+  point) lie between those of the left-hand and right-hand characters. Thus
+  "a-z" (or "z-a") indicates all lower-case Roman alphabetic letters;
+  "^A-Za-z0-9_", all non-word characters.
+
+**set_backlight_string( [string] )**
+  Applies the given string, which should be in the format of the
+  nedit*backlightCharTypes X resource, to the current text window, turning on
+  backlighting.  If the value of the string passed is "default", or if no
+  parameter is passed, the nedit.backlightCharTypes X resource's own value will
+  be used.  If the empty string, "", is passed, backlighting will be turned
+  off.
 
 **set_cursor_pos( position )**
   Set the cursor position for the current window.
diff -ur nedit_official/source/help.c nedit_mod/source/help.c
--- nedit_official/source/help.c	2007-12-29 01:51:50.000000000 +0100
+++ nedit_mod/source/help.c	2008-03-12 02:36:51.000000000 +0100
@@ -399,7 +399,7 @@
 
         if (style == STL_NM_LINK)
             HelpStyleInfo[STYLE_INDEX(style)].color =
-                AllocColor(parent, GetPrefHelpLinkColor(), &r, &g, &b);
+                AllocFgColor(parent, GetPrefHelpLinkColor(), &r, &g, &b);
     }
 }
 
diff -ur nedit_official/source/highlight.c nedit_mod/source/highlight.c
--- nedit_official/source/highlight.c	2008-01-04 23:11:03.000000000 +0100
+++ nedit_mod/source/highlight.c	2008-03-14 23:37:16.000000000 +0100
@@ -775,12 +775,12 @@
       p->isBold = FontOfNamedStyleIsBold(pat->style); \
       p->isItalic = FontOfNamedStyleIsItalic(pat->style); \
       /* And now for the more physical stuff */ \
-      p->color = AllocColor(window->textArea, p->colorName, &r, &g, &b); \
+      p->color = AllocFgColor(window->textArea, p->colorName, &r, &g, &b); \
       p->red = r; \
       p->green = g; \
       p->blue = b; \
       if (p->bgColorName) { \
-        p->bgColor = AllocColor(window->textArea, p->bgColorName, &r, &g, &b); \
+        p->bgColor = AllocBgColor(window->textArea, p->bgColorName, &r,&g,&b); \
         p->bgRed = r; \
         p->bgGreen = g; \
         p->bgBlue = b; \
@@ -1956,35 +1956,37 @@
 }
 
 /*
-** use this canned function to call AllocColor() when
+** use these canned function to call AllocFg/BgColor() when
 ** the r, g & b components is not needed, thus saving
 ** the little hassle of creating the dummy variable.
 */
-Pixel AllocateColor(Widget w, const char *colorName)
+Pixel AllocateFgColor(Widget w, const char *colorName)
 {
     int dummy;
     
-    return AllocColor(w, colorName, &dummy, &dummy, &dummy);
+    return AllocFgColor(w, colorName, &dummy, &dummy, &dummy);
+}
+
+Pixel AllocateBgColor(Widget w, const char *colorName)
+{
+    int dummy;
+    
+    return AllocBgColor(w, colorName, &dummy, &dummy, &dummy);
 }
 
 /*
 ** Allocate a read-only (shareable) colormap cell for a named color, from the
 ** the default colormap of the screen on which the widget (w) is displayed. If
 ** the colormap is full and there's no suitable substitute, print an error on
-** stderr, and return the widget's foreground color as a backup.
+** stderr, and return the passed default color as a backup.
 */
+/* WE SHOULD REALLY PUT THIS ELSEWHERE - THE textDisp WIDGET USES IT TOO */
 
-Pixel AllocColor(Widget w, const char *colorName, int *r, int *g, int *b)
+Pixel AllocFgColor(Widget w, const char *colorName, int *r, int *g, int *b)
 {
-    XColor       colorDef;
-    XColor      *allColorDefs;
-    Display     *display = XtDisplay(w);
     Colormap     cMap;
-    Pixel        foreground, bestPixel;
-    double       small = 1.0e9;
+    Pixel    foreground;
     int          depth;
-    unsigned int ncolors;
-    unsigned long i, best = 0;    /* pixel value */
     
     /* Get the correct colormap for compatability with the "best" visual
        feature in 5.2.  Default visual of screen is no good here. */
@@ -1995,18 +1997,50 @@
                   XtNforeground, &foreground,
                   NULL);
 
-    bestPixel = foreground; /* Our last fallback */
+    return AllocColorDef(w, colorName, r, g, b, cMap, depth, foreground);
+}
+
+Pixel AllocBgColor(Widget w, const char *colorName, int *r, int *g, int *b)
+{
+    Colormap cMap;
+    Pixel    background;
+    int      depth;
+
+    /* Get the correct colormap for compatability with the "best" visual
+       feature in 5.2.  Default visual of screen is no good here. */
+
+    XtVaGetValues(w,
+                  XtNcolormap,   &cMap,
+                  XtNdepth,      &depth,
+                  XtNbackground, &background,
+                  NULL);
+
+    return AllocColorDef(w, colorName, r, g, b, cMap, depth, background);
+}
+
+Pixel AllocColorDef(Widget w, const char *colorName, int *r, int *g, int *b,
+    Colormap cMap, int depth, Pixel defaultColor)
+{
+    XColor       colorDef;
+    XColor      *allColorDefs;
+    Display     *display = XtDisplay(w);
+    Pixel        bestPixel;
+    double       small = 1.0e9;
+    unsigned int ncolors;
+    unsigned long i, best = 0;    /* pixel value */
+
+    bestPixel = defaultColor; /* Our last fallback */
 
     /* First, check for valid syntax */        
     if (! XParseColor(display, cMap, colorName, &colorDef)) {
         fprintf(stderr, "NEdit: Color name %s not in database\n",  colorName);
-        colorDef.pixel = foreground;
+        colorDef.pixel = defaultColor;
         if (XQueryColor(display, cMap, &colorDef)) {
             *r = colorDef.red;
 	    *g = colorDef.green;
 	    *b = colorDef.blue;
         }
-        return foreground;
+        return defaultColor;
     }
 
     /* Attempt allocation of the exact color. */
@@ -2019,21 +2053,17 @@
 
     /* ---------- Allocation failed, the colormap may be full. ---------- */
 
-#if 0
-    printf("Couldn't allocate %d %d %d\n", colorDef.red, colorDef.green, colorDef.blue);
-#endif
- 
     /* We can't do the nearest-match on other than 8 bit visuals because
        it just takes too long.  */
 
     if (depth > 8) {             /* Oh no! */
-        colorDef.pixel = foreground;
+        colorDef.pixel = defaultColor;
         if (XQueryColor(display, cMap, &colorDef)) {
 	    *r = colorDef.red;
 	    *g = colorDef.green;
 	    *b = colorDef.blue;
         }
-        return foreground;
+        return defaultColor;
     }
 
     /* Get the entire colormap so we can find the closest one. */
@@ -2065,13 +2095,6 @@
     if (XAllocColor(display, cMap, &allColorDefs[best]))
         bestPixel = allColorDefs[best].pixel;
 
-#if 0
-    printf("Got %d %d %d, ", allColorDefs[best].red,
-                             allColorDefs[best].green,
-                             allColorDefs[best].blue);
-    printf("That's %f off\n", small);
-#endif
-
     *r = allColorDefs[best].red;
     *g = allColorDefs[best].green;
     *b = allColorDefs[best].blue;
@@ -2080,6 +2103,19 @@
 }
 
 /*
+** Return true if the color name supplied appears valid.
+*/
+int AllocColorNameIsValid(Widget w, const char *colorName)
+{
+    Colormap cMap;
+    Display *display = XtDisplay(w);
+    XColor colorDef;
+
+    XtVaGetValues(w, XtNcolormap, &cMap, NULL);
+    return XParseColor(display, cMap, colorName, &colorDef);
+}
+
+/*
 ** Get the character before position "pos" in buffer "buf"
 */
 static char getPrevChar(textBuffer *buf, int pos)
diff -ur nedit_official/source/highlight.h nedit_mod/source/highlight.h
--- nedit_official/source/highlight.h	2008-01-04 23:11:03.000000000 +0100
+++ nedit_mod/source/highlight.h	2008-03-14 23:37:21.000000000 +0100
@@ -70,8 +70,13 @@
 void RemoveWidgetHighlight(Widget widget);
 void UpdateHighlightStyles(WindowInfo *window);
 int TestHighlightPatterns(patternSet *patSet);
-Pixel AllocateColor(Widget w, const char *colorName);
-Pixel AllocColor(Widget w, const char *colorName, int *r, int *g, int *b);
+Pixel AllocateFgColor(Widget w, const char *colorName);
+Pixel AllocateBgColor(Widget w, const char *colorName);
+Pixel AllocFgColor(Widget w, const char *colorName, int *r, int *g, int *b);
+Pixel AllocBgColor(Widget w, const char *colorName, int *r, int *g, int *b);
+Pixel AllocColorDef(Widget w, const char *colorName, int *r, int *g, int *b,
+    Colormap cMap, int depth, Pixel defaultColor);
+int AllocColorNameIsValid(Widget w, const char *colorName);
 void* GetHighlightInfo(WindowInfo *window, int pos);
 highlightPattern *FindPatternOfWindow(WindowInfo *window, char *name);
 int HighlightCodeOfPos(WindowInfo *window, int pos);
diff -ur nedit_official/source/macro.c nedit_mod/source/macro.c
--- nedit_official/source/macro.c	2007-10-04 18:04:25.000000000 +0200
+++ nedit_mod/source/macro.c	2008-03-12 02:44:13.000000000 +0100
@@ -253,10 +253,10 @@
     	DataValue *result, char **errMsg);
 static int splitMS(WindowInfo *window, DataValue *argList, int nArgs,
     	DataValue *result, char **errMsg);
-/* DISASBLED for 5.4 
 static int setBacklightStringMS(WindowInfo *window, DataValue *argList,
 	int nArgs, DataValue *result, char **errMsg);
-*/
+static int setBacklightMS(WindowInfo *window, DataValue *argList,
+	int nArgs, DataValue *result, char **errMsg);
 static int cursorMV(WindowInfo *window, DataValue *argList, int nArgs,
     	DataValue *result, char **errMsg);
 static int lineMV(WindowInfo *window, DataValue *argList, int nArgs,
@@ -352,13 +352,12 @@
 static int wrongNArgsErr(char **errMsg);
 static int tooFewArgsErr(char **errMsg);
 static int strCaseCmp(char *str1, char *str2);
+static char *intAsStr(int val, char *res);
 static int readIntArg(DataValue dv, int *result, char **errMsg);
 static int readStringArg(DataValue dv, char **result, char *stringStorage,
     	char **errMsg);
-/* DISABLED FOR 5.4
 static int backlightStringMV(WindowInfo *window, DataValue *argList,
 	int nArgs, DataValue *result, char **errMsg);
-*/
 static int rangesetListMV(WindowInfo *window, DataValue *argList,
 	int nArgs, DataValue *result, char **errMsg);
 static int versionMV(WindowInfo* window, DataValue* argList, int nArgs,
@@ -416,7 +415,7 @@
         shellCmdMS, stringToClipboardMS, clipboardToStringMS, toupperMS,
         tolowerMS, listDialogMS, getenvMS,
         stringCompareMS, splitMS, calltipMS, killCalltipMS,
-/* DISABLED for 5.4        setBacklightStringMS,*/
+        setBacklightStringMS, setBacklightMS,
         rangesetCreateMS, rangesetDestroyMS,
         rangesetAddMS, rangesetSubtractMS, rangesetInvertMS,
         rangesetInfoMS, rangesetRangeMS, rangesetIncludesPosMS,
@@ -435,7 +434,7 @@
         "shell_command", "string_to_clipboard", "clipboard_to_string",
         "toupper", "tolower", "list_dialog", "getenv",
         "string_compare", "split", "calltip", "kill_calltip",
-/* DISABLED for 5.4        "set_backlight_string", */
+        "set_backlight_string", "set_backlight",
         "rangeset_create", "rangeset_destroy",
         "rangeset_add", "rangeset_subtract", "rangeset_invert",
         "rangeset_info", "rangeset_range", "rangeset_includes",
@@ -457,7 +456,7 @@
         minFontWidthMV, maxFontWidthMV, topLineMV, numDisplayLinesMV,
         displayWidthMV, activePaneMV, nPanesMV, emptyArrayMV,
         serverNameMV, calltipIDMV,
-/* DISABLED for 5.4        backlightStringMV, */
+        backlightStringMV,
 	rangesetListMV, versionMV
     };
 #define N_SPECIAL_VARS (sizeof SpecialVars/sizeof *SpecialVars)
@@ -475,7 +474,7 @@
         "$min_font_width", "$max_font_width", "$top_line", "$n_display_lines",
         "$display_width", "$active_pane", "$n_panes", "$empty_array",
         "$server_name", "$calltip_ID",
-/* DISABLED for 5.4       "$backlight_string", */
+        "$backlight_string",
         "$rangeset_list", "$VERSION"
     };
 
@@ -4025,12 +4024,214 @@
 }
 
 /*
+** Decompose a backlight string into its component parts. The string is modified
+** and used as a holding place for color names.
+*/
+static Boolean parseBacklightString(Widget w, char *backlightString,
+        char *bgtable[257], char *fgtable[257], char **cols, long maxCols,
+        int testCols, char **errMsg)
+{
+    long lo, hi, i;
+    char *s, *pos, *semicol;
+    size_t was_semicol;
+    Boolean is_good;
+    char inClass[256];
+
+    *errMsg = NULL;
+
+    for (i = 0; i <= 256; ++i)
+        bgtable[i] = fgtable[i] = cols[i] = NULL;
+
+    s = backlightString;
+    if (!s || !*s)
+        return True;
+
+    /* The format of the class string s is:
+            low[-high]{,low[-high]}:color{;low-high{,low[-high]}:color}
+        eg
+            32-255:#f0f0f0;1-31,127:red;128-159:orange/brown;9-13:#e5e5e5
+       Parse the backlight string to put colors into the fg/bg tables */
+
+    while (s) {
+        is_good = True;
+
+        was_semicol = 0;
+        if ((semicol = (char *)strchr(s, ';'))) {
+            *semicol = '\0';    /* null-terminate low[-high]:color clause */
+            was_semicol = 1;
+        }
+
+        memset(inClass, 0, 256);    /* clear flags */
+
+        /* loop over ranges before the color spec, flagging them in inClass[] */
+        for (lo = strtol(s, &pos, 0); is_good; lo = strtol(s, &pos, 0)) {
+            is_good = (s < pos);
+            s = pos;
+            hi = lo;
+            if (s && *s == '-') {
+                hi = strtol(++s, &pos, 0);  /* get end of range */
+                is_good &= (s < pos);
+            }
+            is_good &= (pos && 0 <= lo && lo <= hi && hi <= 255);
+            if (is_good)
+                while (lo <= hi)
+                    inClass[lo++] = 1;
+            if (*pos != ',')
+                break;
+            s = pos + 1;
+        }
+        if (!is_good)
+            M_FAILURE("%s error parsing a backlight string character range");
+
+        /* right: we know the characters; now read the bgcolor or
+           fgcolor/bgcolor combination */
+        if ((is_good = (is_good && *pos == ':'))) {
+            char *slash = (char *)strchr(s, '/');
+            char *fgname, *bgname;
+            /* is it a two-part color? */
+            if (slash) {
+                *slash = '\0';
+                fgname = pos + 1;
+                bgname = slash + 1;
+            }
+            else {
+                fgname = NULL;
+                bgname = pos + 1;
+            }
+            if (bgname && !*bgname)
+                bgname = NULL;
+            if (fgname && !*fgname)
+                fgname = NULL;
+            /* have we see either color before? choose earliest occurrence */
+            if (fgname) {
+                for (i = 0; i < maxCols && cols[i]; ++i) {
+                    if (strcmp(cols[i], fgname) == 0) {
+                        fgname = cols[i];
+                        break;
+                    }
+                }
+                if (i == maxCols)
+                    M_FAILURE("%s found too many colors in backlight string");
+                else if (fgname != cols[i]) {
+                    cols[i] = fgname;
+                    if (testCols && !AllocColorNameIsValid(w, fgname)) {
+                        M_FAILURE("%s bad foreground color in backlight string");
+                    }
+                }
+            }
+            if (bgname) {
+                for (i = 0; i < maxCols && cols[i]; ++i) {
+                    if (strcmp(cols[i], bgname) == 0) {
+                        bgname = cols[i];
+                        break;
+                    }
+                }
+                if (i == maxCols)
+                    M_FAILURE("%s found too many colors in backlight string");
+                else if (bgname != cols[i]) {
+                    cols[i] = bgname;
+                    if (testCols && !AllocColorNameIsValid(w, bgname)) {
+                        M_FAILURE("%s bad background color in backlight string");
+                    }
+                }
+            }
+            /* now store these for every char flagged in inClass[] */
+            for (i = 0; i < 256; ++i) {
+                if (inClass[i]) {
+                    bgtable[i] = bgname;
+                    fgtable[i] = fgname;
+                }
+            }
+        }
+        /* did something break? */
+        if (!is_good)
+            M_FAILURE("%s found an invalid backlight string");
+
+        /* so that's a completed character color class... move on */
+        s = semicol + was_semicol;
+    }
+
+    return True;
+}
+
+/*
+** Copy a string into another, or pretend to do so. Returns the source string's
+** length. If the destination is valid, its pointer (*to) is changed to point
+** to the end of the copy.
+*/
+static size_t strcpyAndMove(char **to, const char *from)
+{
+    size_t res = from ? strlen(from) : 0;
+    if (*to && res) {
+        strcpy(*to, from);
+        *to += res;
+    }
+    return from ? strlen(from) : 0;
+}
+
+/*
+** Construct a backlight string from the colors represented in the two tables
+** in destination buffer dest. If dest is null, just measure the required
+** length in *len (not including the terminating NUL).
+*/
+static char *buildBacklightString(char *bgtable[257], char *fgtable[257],
+        char *dest, size_t *length)
+{
+    size_t len = 0;
+    char done[257];
+    char intStore[TYPE_INT_STR_SIZE(int)];
+    char *final = dest;
+    char *bgcol, *fgcol;
+    size_t i;
+
+    *length = 0;
+    memset(done, 0, sizeof done);
+    for (i = 0; i < 256; ++i) {
+        if (done[i])
+            continue;
+        bgcol = bgtable[i];
+        fgcol = fgtable[i];
+        if (bgcol || fgcol) {
+            int lo, hi, j;
+            lo = hi = i;
+            if (len > 0)
+                len += strcpyAndMove(&dest, ";");
+            len += strcpyAndMove(&dest, intAsStr(lo, intStore));
+            for (j = i + 1; j <= 256; ++j) {
+                if (bgcol == bgtable[j] && fgcol == fgtable[j]) {
+                    done[j] = 1;
+                    if (j == hi + 1)    /* still in contiguous range */
+                        ++hi;
+                    else {              /* start new range */
+                        lo = hi = j;
+                        len += strcpyAndMove(&dest, ",");
+                        len += strcpyAndMove(&dest, intAsStr(lo, intStore));
+                    }
+                }
+                else if (hi == j - 1 && lo < hi) { /* ended contiguous range */
+                    len += strcpyAndMove(&dest, "-");
+                    len += strcpyAndMove(&dest, intAsStr(hi, intStore));
+                }
+            }
+            /* output colors for range here */
+            len += strcpyAndMove(&dest, ":");
+            if (fgcol) {
+                len += strcpyAndMove(&dest, fgcol);
+                len += strcpyAndMove(&dest, "/");
+            }
+            len += strcpyAndMove(&dest, bgcol);
+        }
+    }
+    *length = len;
+    return final;
+}
+
+/*
 ** Set the backlighting string resource for the current window. If no parameter
 ** is passed or the value "default" is passed, it attempts to set the preference
 ** value of the resource. If the empty string is passed, the backlighting string
 ** will be cleared, turning off backlighting.
 */
-/* DISABLED for 5.4
 static int setBacklightStringMS(WindowInfo *window, DataValue *argList,
       int nArgs, DataValue *result, char **errMsg)
 {
@@ -4051,12 +4252,198 @@
 
     if (strcmp(backlightString, "default") == 0)
       backlightString = GetPrefBacklightCharTypes();
-    if (backlightString && *backlightString == '\0')  / * empty string param * /
-      backlightString = NULL;                 / * turns of backlighting * /
+    if (backlightString && *backlightString == '\0')  /* empty string param */
+      backlightString = NULL;                 /* turns off backlighting */
 
     SetBacklightChars(window, backlightString);
     return True;
-} */
+}
+
+/*
+** Change the backlighting colors for the current window for a particular range
+** of characters. Returns true if the results were applied or there was nothing
+** to apply; false for invalid colors. If no colors are given, characters of the
+** range will not be backlit; if one color given, it is a background color; if
+** two, they are foreground, background respectively. Colors must be valid,
+** empty strings (for no change) or space-filled only or "none" (for remove).
+*/
+static int setBacklightMS(WindowInfo *window, DataValue *argList,
+      int nArgs, DataValue *result, char **errMsg)
+{
+    char rangeStorage[TYPE_INT_STR_SIZE(int)];
+    char bgcolStorage[TYPE_INT_STR_SIZE(int)];
+    char fgcolStorage[TYPE_INT_STR_SIZE(int)];
+    char *range, *s;
+    char *bgcol, *fgcol;
+    char *backlightString;
+    char *current = window->backlightCharTypes;
+
+    char *bgtable[257]; /* allow a sentinel at the end */
+    char *fgtable[257];
+    /* color names array must have extra space for 2 new names and a sentinel */
+    char *cols[400];
+    int maxCols = (sizeof cols / sizeof *cols) - 3;
+
+    char inClass[256];
+    char invert, sel, res;
+    size_t i, length;
+    Widget w = window->textArea;
+
+    if (nArgs < 1 || 3 < nArgs)
+        return wrongNArgsErr(errMsg);
+
+    bgcol = fgcol = NULL;
+    if (!readStringArg(argList[0], &range, rangeStorage, errMsg))
+        M_FAILURE("%s first argument should be a character range");
+    if (nArgs > 1 && !readStringArg(argList[1], &fgcol, fgcolStorage, errMsg))
+        M_FAILURE("%s second argument should be a background color name");
+    if (nArgs > 2 && !readStringArg(argList[2], &bgcol, bgcolStorage, errMsg))
+        M_FAILURE("%s second argument should be a foreground color name");
+
+    if (nArgs == 2) {
+        /* only have bgcol */
+        bgcol = fgcol;
+        fgcol = NULL;
+    }
+
+    if (bgcol) {
+        if (!*bgcol)
+            bgcol = NULL;
+        else if (strCaseCmp(bgcol, "none") == 0)
+            bgcol = "";
+        else {
+            while (isspace(*bgcol))
+                bgcol++;
+        }
+    }
+
+    if (fgcol) {
+        if (!*fgcol)
+            fgcol = NULL;
+        else if (strCaseCmp(fgcol, "none") == 0)
+            fgcol = "";
+        else {
+            while (isspace(*fgcol))
+                fgcol++;
+        }
+    }
+
+    if (!*range) {
+        /* range has no characters */
+        result->tag = INT_TAG;
+        result->val.n = 1;
+        return True;
+    }
+
+    /* test color validity for fgcol, bgcol (if not null); allow "none" */
+    if (bgcol && *bgcol && !AllocColorNameIsValid(w, bgcol)) {
+        M_FAILURE("%s invalid background color");
+    }
+    if (fgcol && *fgcol && !AllocColorNameIsValid(w, fgcol)) {
+        M_FAILURE("%s invalid foreground color");
+    }
+
+    /* pick up a copy of the current backlighting string: this is read
+       destructively, but kept to hold the color names until we've build the
+       new backlight string */
+    if (!current || !*current)
+        backlightString = NULL;
+    else {
+        backlightString = malloc(strlen(current) + 1);
+        if (!backlightString)
+            M_FAILURE("%s string allocation failure");
+        strcpy(backlightString, current);
+    }
+
+    if (!parseBacklightString(w, backlightString, bgtable, fgtable, cols,
+                              maxCols, 0, errMsg)) {
+        free(backlightString);
+        return False; /* errMsg already set up */
+    }
+
+    cols[maxCols + 2] = cols[maxCols + 1] = cols[maxCols] = NULL;
+    /* do we have bgcol and fgcol already? */
+    if (bgcol) {
+        for (i = 0; i < maxCols && cols[i]; ++i) {
+            if (strcmp(cols[i], bgcol) == 0) {
+                bgcol = cols[i];
+                break;
+            }
+        }
+        if (bgcol != cols[i])
+             cols[i] = bgcol;
+    }
+    if (fgcol) {
+        for (i = 0; i < maxCols && cols[i]; ++i) {
+            if (strcmp(cols[i], fgcol) == 0) {
+                fgcol = cols[i];
+                break;
+            }
+        }
+        if (fgcol != cols[i])
+             cols[i] = fgcol;
+    }
+    /* now handle the character range: this works like the inside of a []
+       in a regex: ^ at start (and if followed by something) means "all except",
+       "-" indicates a range (unless first or last in string) except that the
+       regex char class sequences are not recognised */
+    s = range;
+    invert = (s[0] == '^' && s[1]); /* ie not a '^' on its own */
+    memset(inClass, invert, 256);
+    sel = !invert;
+    s += invert;
+    while (*s) {
+        unsigned ch = (unsigned char)*s++;
+        unsigned nx;
+        inClass[ch] = sel;
+        if (s[0] == '-' && (nx = (unsigned char)s[1])) {
+            /* a range, eg "a-z" or "Z-A": allow ascending and descending */
+            while (ch < nx)
+                inClass[nx--] = sel;
+            while (nx < ch)
+                inClass[nx++] = sel;
+            s += 2;
+        }
+    }
+    /* right: we have flagged the entries in inClass whose colors to set */
+    if (bgcol) {
+        if (!*bgcol)
+            bgcol = NULL;
+        for (i = 0; i < 256; ++i) {
+            if (inClass[i])
+                bgtable[i] = bgcol;
+        }
+    }
+    if (fgcol) {
+        if (!*fgcol)
+            fgcol = NULL;
+        for (i = 0; i < 256; ++i) {
+            if (inClass[i])
+                fgtable[i] = fgcol;
+        }
+    }
+
+    /* now we can construct the backlighting string to apply */
+    res = 1;
+    buildBacklightString(bgtable, fgtable, NULL, &length);
+    s = malloc(length + 1);
+    if (!s)
+        res = 0;
+    else
+        buildBacklightString(bgtable, fgtable, s, &length);
+    /* we can only drop this now - before it held a color names */
+    free(backlightString);
+
+    /* apply backlighting string */
+    SetBacklightChars(window, s);
+
+    /* and free s now */
+    free(s);
+
+    result->tag = INT_TAG;
+    result->val.n = res;
+    return True;
+}
 
 static int cursorMV(WindowInfo *window, DataValue *argList, int nArgs,
     	DataValue *result, char **errMsg)
@@ -4511,7 +4898,6 @@
     return True;
 }
 
-/* DISABLED for 5.4
 static int backlightStringMV(WindowInfo *window, DataValue *argList,
       int nArgs, DataValue *result, char **errMsg)
 {
@@ -4522,7 +4908,7 @@
       backlightString = "";
     AllocNStringCpy(&result->val.str, backlightString);
     return True;
-} */
+}
 
 /* -------------------------------------------------------------------------- */
 
@@ -5719,6 +6105,30 @@
 }
 
 /*
+** Write a number backwards into a buffer, returning the start of the number.
+** It expects a big enough buffer (TYPE_INT_STR_SIZE(int)).
+*/
+static char *intAsStr(int val, char *res)
+{
+    static const char alldigits[] = "9876543210123456789";
+    static const char *digits = &alldigits[9]; /* allow -9 <= index <= 9 */
+    char *pos = &res[TYPE_INT_STR_SIZE(val) - 1];
+    int nonneg = (val >= 0);
+
+    /* the string is built backwards, so start by null terminating it */
+    *pos = 0;
+    do {
+        int val10 = val / 10;
+        *--pos = digits[val - (10 * val10)];
+        val = val10;
+    } while (val != 0);
+
+    *--pos = '-';
+
+    return pos + nonneg;
+}
+
+/*
 ** Get an integer value from a tagged DataValue structure.  Return True
 ** if conversion succeeded, and store result in *result, otherwise
 ** return False with an error message in *errMsg.
@@ -5755,13 +6165,13 @@
 static int readStringArg(DataValue dv, char **result, char *stringStorage,
     	char **errMsg)
 {
+    *result = NULL;
     if (dv.tag == STRING_TAG) {
-    	*result = dv.val.str.rep;
-    	return True;
+        *result = dv.val.str.rep;
+        return True;
     } else if (dv.tag == INT_TAG) {
-	sprintf(stringStorage, "%d", dv.val.n);
-	*result = stringStorage;
-	return True;
+        *result = intAsStr(dv.val.n, stringStorage);
+        return True;
     }
     *errMsg = "%s called with unknown object";
     return False;
diff -ur nedit_official/source/preferences.c nedit_mod/source/preferences.c
--- nedit_official/source/preferences.c	2008-01-14 21:39:20.000000000 +0100
+++ nedit_mod/source/preferences.c	2008-03-12 02:36:51.000000000 +0100
@@ -4363,9 +4363,9 @@
     origFontName = XmTextGetString(fontTextW);
 
     /* Get the values from the defaults */
-    fgPixel = AllocColor(parent, GetPrefColorName(TEXT_FG_COLOR),
+    fgPixel = AllocFgColor(parent, GetPrefColorName(TEXT_FG_COLOR),
             &dummy, &dummy, &dummy);
-    bgPixel = AllocColor(parent, GetPrefColorName(TEXT_BG_COLOR),
+    bgPixel = AllocBgColor(parent, GetPrefColorName(TEXT_BG_COLOR),
             &dummy, &dummy, &dummy);
 
     newFontName = FontSel(parent, PREF_FIXED, origFontName, fgPixel, bgPixel);
diff -ur nedit_official/source/text.c nedit_mod/source/text.c
--- nedit_official/source/text.c	2008-01-04 23:11:04.000000000 +0100
+++ nedit_mod/source/text.c	2008-03-12 02:36:51.000000000 +0100
@@ -1159,8 +1159,8 @@
     if (new->text.backlightCharTypes != current->text.backlightCharTypes)
     {
         TextDSetupBGClasses((Widget)new, new->text.backlightCharTypes,
-                &new->text.textD->bgClassPixel, &new->text.textD->bgClass,
-                new->text.textD->bgPixel);
+                &new->text.textD->bgClassPixel, &new->text.textD->fgClassPixel,
+                &new->text.textD->bgClass, new->text.textD->bgPixel);
         redraw = True;
     }
     
diff -ur nedit_official/source/textDisp.c nedit_mod/source/textDisp.c
--- nedit_official/source/textDisp.c	2008-01-04 23:31:48.000000000 +0100
+++ nedit_mod/source/textDisp.c	2008-03-12 02:36:51.000000000 +0100
@@ -176,7 +176,8 @@
 static void resetAbsLineNum(textDisp *textD);
 static int measurePropChar(const textDisp* textD, const char c,
         const int colNum, const int pos);
-static Pixel allocBGColor(Widget w, char *colorName, int *ok);
+static Pixel allocBgColor(Widget w, char *colorName, int *ok);
+static Pixel allocFgColor(Widget w, char *colorName, int *ok);
 static Pixel getRangesetColor(textDisp *textD, int ind, Pixel bground);
 static void textDRedisplayRange(textDisp *textD, int start, int end);
 
@@ -254,9 +255,10 @@
     for (i=1; i<textD->nVisibleLines; i++)
     	textD->lineStarts[i] = -1;
     textD->bgClassPixel = NULL;
+    textD->fgClassPixel = NULL;
     textD->bgClass = NULL;
     TextDSetupBGClasses(widget, bgClassString, &textD->bgClassPixel,
-          &textD->bgClass, bgPixel);
+          &textD->fgClassPixel, &textD->bgClass, bgPixel);
     textD->suppressResync = 0;
     textD->nLinesDeleted = 0;
     textD->modifyingTabDist = 0;
@@ -322,6 +324,7 @@
     while (TextDPopGraphicExposeQueueEntry(textD)) {
     }
     XtFree((char *)textD->bgClassPixel);
+    XtFree((char *)textD->fgClassPixel);
     XtFree((char *)textD->bgClass);
     XtFree((char *)textD);
 }
@@ -1975,6 +1978,11 @@
     if (gc == textD->styleGC) {
         /* we have work to do */
         styleTableEntry *styleRec;
+        Pixel backFG;
+        Pixel backBG;
+        int isBackLight = (style & BACKLIGHT_MASK) && !(style & FILL_MASK);
+        int backlightInd = (style & BACKLIGHT_MASK) >> BACKLIGHT_SHIFT;
+
         /* Set font, color, and gc depending on style.  For normal text, GCs
            for normal drawing, or drawing within a selection or highlight are
            pre-allocated and pre-configured.  For syntax highlighting, GCs are
@@ -2004,11 +2012,23 @@
                           (style&RANGESET_MASK)>>RANGESET_SHIFT,
                             bground) :
             styleRec && styleRec->bgColorName ? styleRec->bgColor :
-            (style & BACKLIGHT_MASK) && !(style & FILL_MASK) ?
-                      textD->bgClassPixel[(style>>BACKLIGHT_SHIFT) & 0xff] :
+            isBackLight ?
+                      textD->bgClassPixel[backlightInd] :
             textD->bgPixel;
-        if (fground == bground) /* B&W kludge */
+        /* Foreground color is backlight foreground if the stored backlight
+           foreground is not the same as default foreground - Note this
+           overrides the highlight style */
+        if (isBackLight) {
+            backFG = textD->fgClassPixel[(style>>BACKLIGHT_SHIFT) & 0xff];
+            backBG = textD->bgClassPixel[(style>>BACKLIGHT_SHIFT) & 0xff];
+            if (backFG != backBG)
+                fground = backFG;
+        }
+        if (fground == bground) { /* B&W kludge */
             fground = textD->bgPixel;
+            if (fground == bground) /* check again! */
+                fground = textD->fgPixel;
+        }
         /* set up gc for clearing using the foreground color entry */
         gcValues.foreground = gcValues.background = bground;
         XChangeGC(XtDisplay(textD->w), gc,
@@ -3652,17 +3672,19 @@
 }
 
 /**********************  Backlight Functions ******************************/
-/*
-** Allocate a read-only (shareable) colormap cell for a named color, from the
-** the default colormap of the screen on which the widget (w) is displayed. If
-** the colormap is full and there's no suitable substitute, print an error on
-** stderr, and return the widget's background color as a backup.
-*/
-static Pixel allocBGColor(Widget w, char *colorName, int *ok)
+
+static Pixel allocFgColor(Widget w, char *colorName, int *ok)
+{
+    int r,g,b;
+    *ok = 1;
+    return AllocFgColor(w, colorName, &r, &g, &b);
+}
+
+static Pixel allocBgColor(Widget w, char *colorName, int *ok)
 {
     int r,g,b;
     *ok = 1;
-    return AllocColor(w, colorName, &r, &g, &b);
+    return AllocBgColor(w, colorName, &r, &g, &b);
 }
 
 static Pixel getRangesetColor(textDisp *textD, int ind, Pixel bground)
@@ -3682,7 +3704,7 @@
       if (valid == 0) {
           color_name = RangesetTableGetColorName(tab, ind);
           if (color_name)
-              color = allocBGColor(textD->w, color_name, &valid);
+              color = allocBgColor(textD->w, color_name, &valid);
           RangesetTableAssignColorPixel(tab, ind, color, valid);
       }
       if (valid > 0) {
@@ -3702,11 +3724,13 @@
 ** there'll be a pressing need. I suppose the scanning of the specification
 ** could be better too, but then, who cares!
 */
-void TextDSetupBGClasses(Widget w, XmString str, Pixel **pp_bgClassPixel,
+void TextDSetupBGClasses(Widget w, XmString str,
+      Pixel **pp_bgClassPixel, Pixel **pp_fgClassPixel,
       unsigned char **pp_bgClass, Pixel bgPixelDefault)
 {
     unsigned char bgClass[256];
     Pixel bgClassPixel[256];
+    Pixel fgClassPixel[256];
     int class_no = 0;
     char *semicol;
     char *s = (char *)str;
@@ -3717,8 +3741,10 @@
 
     XtFree((char *)*pp_bgClass);
     XtFree((char *)*pp_bgClassPixel);
+    XtFree((char *)*pp_fgClassPixel);
 
     *pp_bgClassPixel = NULL;
+    *pp_fgClassPixel = NULL;
     *pp_bgClass = NULL;
 
     if (!s)
@@ -3726,8 +3752,10 @@
 
     /* default for all chars is class number zero, for standard background */
     memset(bgClassPixel, 0, sizeof bgClassPixel);
+    memset(fgClassPixel, 0, sizeof fgClassPixel);
     memset(bgClass, 0, sizeof bgClass);
     bgClassPixel[0] = bgPixelDefault;
+    fgClassPixel[0] = bgPixelDefault;
     /* since class no == 0 in a "style" has no set bits in BACKLIGHT_MASK
        (see styleOfPos()), when drawString() is called for text with a
        backlight class no of zero, bgClassPixel[0] is never consulted, and
@@ -3736,7 +3764,7 @@
     /* The format of the class string s is:
               low[-high]{,low[-high]}:color{;low-high{,low[-high]}:color}
           eg
-              32-255:#f0f0f0;1-31,127:red;128-159:orange;9-13:#e5e5e5
+              32-255:#f0f0f0;1-31,127:red;128-159:orange/brown;9-13:#e5e5e5
        where low and high represent a character range between ordinal
        ASCII values. Using strtol() allows automatic octal, dec and hex
        reading of low and high. The example format sets backgrounds as follows:
@@ -3745,7 +3773,7 @@
               char  14 - 31   colored red     (control characters)
               char  32 - 126  colored #f0f0f0
               char 127        colored red     (delete character)
-              char 128 - 159  colored orange  ("shifted" control characters)
+              char 128 - 159  colored orange/brown ("shifted" control chars)
               char 160 - 255  colored #f0f0f0
        Notice that some of the later ranges overwrite the class values defined
        for earlier ones (eg the first clause, 32-255:#f0f0f0 sets the DEL
@@ -3767,7 +3795,7 @@
            is_good;
            lo = hi = strtol(pos + 1, &pos, 0)) {
           if (pos && *pos == '-')
-              hi = strtol(pos + 1, &pos, 0);  /* get end of range */
+              hi = strtol(pos + 1, &pos, 0);    /* get end of range */
           is_good = (pos && 0 <= lo && lo <= hi && hi <= 255);
           if (is_good)
               while (lo <= hi)
@@ -3776,8 +3804,24 @@
               break;
       }
       if ((is_good = (is_good && *pos == ':'))) {
-          is_good = (*pos++ != '\0');         /* pos now points to color */
-          bgClassPixel[class_no] = allocBGColor(w, pos, &dummy);
+          char *slash = (char *)strchr(s, '/');
+          char *fgname, *bgname;
+          Pixel fg = bgPixelDefault;
+          Pixel bg = bgPixelDefault;
+          is_good = (*pos++ != '\0');           /* pos now points to color */
+          fgname = bgname = pos;
+          /* is it a two-part color? */
+          if (slash) {
+              *slash = '\0';
+              bgname = slash + 1;
+          }
+          fg = bg = allocBgColor(w, bgname, &dummy);
+          if (pos != bgname && *pos)
+              fg = allocFgColor(w, fgname, &dummy);
+          bgClassPixel[class_no] = bg;
+          fgClassPixel[class_no] = fg;
+          if (slash)
+              *slash = '/';                     /* put this back */
       }
       if (!is_good) {
           /* complain? this class spec clause (in string s) was faulty */
@@ -3794,11 +3838,14 @@
     class_no++;                     /* bigger than all valid class_nos */
     *pp_bgClass = (unsigned char *)XtMalloc(256);
     *pp_bgClassPixel = (Pixel *)XtMalloc(class_no * sizeof (Pixel));
-    if (!*pp_bgClass || !*pp_bgClassPixel) {
+    *pp_fgClassPixel = (Pixel *)XtMalloc(class_no * sizeof (Pixel));
+    if (!*pp_bgClass || !*pp_bgClassPixel || !*pp_fgClassPixel) {
         XtFree((char *)*pp_bgClass);
         XtFree((char *)*pp_bgClassPixel);
+        XtFree((char *)*pp_fgClassPixel);
         return;
     }
     memcpy(*pp_bgClass, bgClass, 256);
     memcpy(*pp_bgClassPixel, bgClassPixel, class_no * sizeof (Pixel));
+    memcpy(*pp_fgClassPixel, fgClassPixel, class_no * sizeof (Pixel));
 }
diff -ur nedit_official/source/textDisp.h nedit_mod/source/textDisp.h
--- nedit_official/source/textDisp.h	2008-01-04 23:11:04.000000000 +0100
+++ nedit_mod/source/textDisp.h	2008-03-12 02:36:51.000000000 +0100
@@ -142,7 +142,8 @@
           highlightBGPixel;             /*    flashing matching parens    */
     Pixel lineNumFGPixel;   	    	/* Color for drawing line numbers */
     Pixel cursorFGPixel;
-    Pixel *bgClassPixel;		/* table of colors for each BG class */
+    Pixel *bgClassPixel;		/* table of BG cols for each BG class */
+    Pixel *fgClassPixel;		/* table of FG color overrides */
     unsigned char *bgClass;		/* obtains index into bgClassPixel[] */
     
     Widget calltipW;                    /* The Label widget for the calltip */
@@ -218,7 +219,8 @@
 int TextDCountBackwardNLines(textDisp *textD, int startPos, int nLines);
 int TextDCountLines(textDisp *textD, int startPos, int endPos,
     	int startPosIsLineStart);
-void TextDSetupBGClasses(Widget w, XmString str, Pixel **pp_bgClassPixel,
+void TextDSetupBGClasses(Widget w, XmString str,
+      Pixel **pp_bgClassPixel, Pixel **pp_fgClassPixel,
 	unsigned char **pp_bgClass, Pixel bgPixelDefault);
 void TextDSetLineNumberArea(textDisp *textD, int lineNumLeft, int lineNumWidth,
 	int textLeft);
diff -ur nedit_official/source/window.c nedit_mod/source/window.c
--- nedit_official/source/window.c	2008-01-05 03:24:13.000000000 +0100
+++ nedit_mod/source/window.c	2008-03-12 02:36:51.000000000 +0100
@@ -829,8 +829,8 @@
        we try to use the 'standard' color */
     tooltipLabel = XtNameToWidget(tab, "*BubbleLabel");
     XtVaSetValues(tooltipLabel,
-    	    XmNbackground, AllocateColor(tab, GetPrefTooltipBgColor()),
-    	    XmNforeground, AllocateColor(tab, NEDIT_DEFAULT_FG),
+    	    XmNbackground, AllocateBgColor(tab, GetPrefTooltipBgColor()),
+    	    XmNforeground, AllocateFgColor(tab, NEDIT_DEFAULT_FG),
 	    NULL);
 
     /* put borders around tooltip. BubbleButton use 
@@ -1858,21 +1858,21 @@
         const char *hiliteBg, const char *lineNoFg, const char *cursorFg)
 {
     int i, dummy;
-    Pixel   textFgPix   = AllocColor( window->textArea, textFg, 
+    Pixel   textFgPix   = AllocFgColor( window->textArea, textFg, 
                     &dummy, &dummy, &dummy),
-            textBgPix   = AllocColor( window->textArea, textBg, 
+            textBgPix   = AllocBgColor( window->textArea, textBg, 
                     &dummy, &dummy, &dummy),
-            selectFgPix = AllocColor( window->textArea, selectFg, 
+            selectFgPix = AllocFgColor( window->textArea, selectFg, 
                     &dummy, &dummy, &dummy),
-            selectBgPix = AllocColor( window->textArea, selectBg, 
+            selectBgPix = AllocBgColor( window->textArea, selectBg, 
                     &dummy, &dummy, &dummy),
-            hiliteFgPix = AllocColor( window->textArea, hiliteFg, 
+            hiliteFgPix = AllocFgColor( window->textArea, hiliteFg, 
                     &dummy, &dummy, &dummy),
-            hiliteBgPix = AllocColor( window->textArea, hiliteBg, 
+            hiliteBgPix = AllocBgColor( window->textArea, hiliteBg, 
                     &dummy, &dummy, &dummy),
-            lineNoFgPix = AllocColor( window->textArea, lineNoFg, 
+            lineNoFgPix = AllocFgColor( window->textArea, lineNoFg, 
                     &dummy, &dummy, &dummy),
-            cursorFgPix = AllocColor( window->textArea, cursorFg, 
+            cursorFgPix = AllocFgColor( window->textArea, cursorFg, 
                     &dummy, &dummy, &dummy);
     textDisp *textD;
 
