From 6c1e6612adf401fe50541e28f64791fa8c2cae20 Mon Sep 17 00:00:00 2001 From: Chris Allegretta Date: Sat, 19 Jan 2002 16:52:34 +0000 Subject: [PATCH] - General - Added multiple-line regex support. Format in .nanorc is start=regex end=regex. Cleaned up nanorc:parse_colors(), added parse_next_regex(), changes to edit_add in winio.c(), changes to colortype, cleaning up some old cruft git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@1021 35c25a1d-7b9e-4130-9fde-d3aeb78583b8 --- ChangeLog | 5 + nano.h | 12 +- nanorc.sample | 24 ++-- rcfile.c | 323 ++++++++++++++++++++++++++------------------------ winio.c | 159 ++++++++++++++++++++----- 5 files changed, 319 insertions(+), 204 deletions(-) diff --git a/ChangeLog b/ChangeLog index b69005d6..ac77f825 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,10 @@ CVS code - - Better partial word checking code. New function search.c:is_whole_word(), changes to findnextstr(), and nano.c:do_int_spell_fix() (Rocco Corsi). + - Added multiple-line regex support. Format in .nanorc is + start="regex" end="regex". Cleaned up nanorc:parse_colors(), + added parse_next_regex(), changes to edit_add in winio.c(), + changes to colortype, cleaning up some old cruft. - color.c: do_colorinit() - Moved some comments and braces around so color can work @@ -39,6 +43,7 @@ CVS code - General - Took silly variables being passed everywhere like lineno and filename and made them static variables. + - Re-indented. rcfile_error() - Now automatically prpends the "error in line blah at foo" message to error messages. diff --git a/nano.h b/nano.h index 7947fe87..26e10a6e 100644 --- a/nano.h +++ b/nano.h @@ -122,12 +122,12 @@ typedef struct rcoption { #define COLORSTRNUM 16 typedef struct colortype { - int fg; - int bg; - int bright; - int pairnum; - char *start; - char *end; + int fg; /* fg color */ + int bg; /* bg color */ + int bright; /* Is this color A_BOLD? */ + int pairnum; /* Color pair number used for this fg/bg */ + char *start; /* Start (or all) of the regex string */ + char *end; /* End of the regex string */ struct colortype *next; } colortype; diff --git a/nanorc.sample b/nanorc.sample index 8a40fc83..78628bd3 100644 --- a/nanorc.sample +++ b/nanorc.sample @@ -27,7 +27,7 @@ # set fill -8 # Use this tab size instead of the default -# set tabsize 8 +# set tabsize 4 # Use this spelling checker instead of the default one # set speller aspell @@ -58,23 +58,25 @@ # # Color setup -# Format: color foreground,background regex [regex...] +# Format: color foreground,background "regex" ["regex"...] # # Legal colors are: white, black, red, blue, green, yellow, purple, cyan # You may use the prefix "bright" to mean a stronger color highlight # +# To use multi-line regexes use the start="regex" end="regex" format. +# # If your system supports transparency, not specifying a background # color will use a transparent color. If you don't want this, be sure # to set the background color to black or white. # -#color brightred float\ char\ int\ void\ NULL [A-Z_]\{2,\} static -#color brightred [\ ]struct ^struct if\ while[\ \n\(] do[\ \n\(] else[\ \n] case\ switch\ break; -#color brightcyan #define #include #ifn*def #endif #elif #else +#color brightred "float " "char " "int " "void " "NULL" "[A-Z_]\{2,\}" +#color brightred "static" "const" "[\ ]struct" "^struct" "if " "while[\ \n\(]" +#color brightred "do[\ \n\(]" "else[\ \n]" "case " "switch " "break;" +#color brightcyan "#define" "#include" "#ifn*def" "#endif" "#elif" "#else" -# You will in general want your comments and strings to come last, because -# syntax highlighting rules will be applied in the order they are read in +#You will in general want your comments and strings to come last, becase +#syntax highlighting rules will be applied in the order they are read in -#color brightyellow <.*> ".*" -#color brightblue /\*.**/ -# multi-line comment hack -#color brightblue /\*.* [^\/][^\*]*\*\/ ^.*\*+*\**$ ^\ *\*\{1,\}\/$ \/\/.* +#color brightyellow "<.*[^=\ ]*>" "\".*\"" +#color brightblue "//.*" +#color brightblue start="/\*.*" end="\*/" diff --git a/rcfile.c b/rcfile.c index ca2320f7..2021c0fd 100644 --- a/rcfile.c +++ b/rcfile.c @@ -40,37 +40,37 @@ #endif #ifndef DISABLE_WRAPJUSTIFY - #define NUM_RCOPTS 19 +#define NUM_RCOPTS 19 #else - #define NUM_RCOPTS 18 +#define NUM_RCOPTS 18 #endif /* Static stuff for the nanorc file */ -rcoption rcopts[NUM_RCOPTS] = -{ -{"regexp", USE_REGEXP}, -{"const", CONSTUPDATE}, -{"autoindent", AUTOINDENT}, -{"cut", CUT_TO_END}, -{"nofollow", FOLLOW_SYMLINKS}, -{"mouse", USE_MOUSE}, -{"operatingdir", 0}, -{"pico", PICO_MODE}, -{"tabsize", 0}, +rcoption rcopts[NUM_RCOPTS] = { + {"regexp", USE_REGEXP}, + {"const", CONSTUPDATE}, + {"autoindent", AUTOINDENT}, + {"cut", CUT_TO_END}, + {"nofollow", FOLLOW_SYMLINKS}, + {"mouse", USE_MOUSE}, + {"operatingdir", 0}, + {"pico", PICO_MODE}, + {"tabsize", 0}, #ifndef DISABLE_WRAPJUSTIFY -{"fill", 0}, + {"fill", 0}, #endif -{"speller", 0}, -{"tempfile", TEMP_OPT}, -{"view", VIEW_MODE}, -{"nowrap", NO_WRAP}, -{"nohelp", NO_HELP}, -{"suspend", SUSPEND}, -{"multibuffer", MULTIBUFFER}, -{"smooth", SMOOTHSCROLL}, -{"keypad", ALT_KEYPAD}}; + {"speller", 0}, + {"tempfile", TEMP_OPT}, + {"view", VIEW_MODE}, + {"nowrap", NO_WRAP}, + {"nohelp", NO_HELP}, + {"suspend", SUSPEND}, + {"multibuffer", MULTIBUFFER}, + {"smooth", SMOOTHSCROLL}, + {"keypad", ALT_KEYPAD} +}; static int errors = 0; static int lineno = 0; @@ -89,8 +89,7 @@ void rcfile_error(char *msg, ...) va_end(ap); fprintf(stderr, _("\nPress return to continue starting nano\n")); - while (getchar() != '\n') - ; + while (getchar() != '\n'); } @@ -113,12 +112,27 @@ void rcfile_msg(char *msg, ...) /* Parse the next word from the string. Returns NULL if we hit EOL */ char *parse_next_word(char *ptr) { - char *prev = " "; + while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n' && *ptr != '\0') + ptr++; + + if (*ptr == '\0') + return NULL; + + /* Null terminate and advance ptr */ + *ptr++ = 0; + + while ((*ptr == ' ' || *ptr == '\t') && *ptr != '\0') + ptr++; + + return ptr; +} - while ((*ptr != ' ' || *prev == '\\') - && *ptr != '\t' && *ptr != '\n' && *ptr != '\0') { +char *parse_next_regex(char *ptr) +{ + char prev = ' '; + while ((*ptr != '"' || prev == '\\') && *ptr != '\n' && *ptr != '\0') { + prev = *ptr; ptr++; - prev = ptr; } if (*ptr == '\0') @@ -131,9 +145,10 @@ char *parse_next_word(char *ptr) ptr++; return ptr; + } -int colortoint(char *colorname, int *bright) +int colortoint(char *colorname, int *bright) { int mcolor = 0; @@ -144,7 +159,7 @@ int colortoint(char *colorname, int *bright) *bright = 1; colorname += 6; } - + if (!strcasecmp(colorname, "green")) mcolor += COLOR_GREEN; else if (!strcasecmp(colorname, "red")) @@ -162,10 +177,10 @@ int colortoint(char *colorname, int *bright) else if (!strcasecmp(colorname, "black")) mcolor += COLOR_BLACK; else { - rcfile_error(_("color %s not understood.\n" - "Valid colors are \"green\", \"red\", \"blue\", \n" - "\"white\", \"yellow\", \"cyan\", \"magenta\" and \n" - "\"black\", with the optional prefix \"bright\".\n")); + rcfile_error(_("color %s not understood.\n" + "Valid colors are \"green\", \"red\", \"blue\", \n" + "\"white\", \"yellow\", \"cyan\", \"magenta\" and \n" + "\"black\", with the optional prefix \"bright\".\n")); exit(1); } @@ -175,11 +190,10 @@ int colortoint(char *colorname, int *bright) #ifdef ENABLE_COLOR /* Parse the color stuff into the colorstrings array */ -void parse_colors(FILE *rcstream, char *buf, char *ptr) +void parse_colors(FILE * rcstream, char *buf, char *ptr) { - int i = 0, fg, bg, bright = 0, loopdone = 0; - int expectend = 0; /* Do we expect an end= line? */ - char prev = '\\'; + int fg, bg, bright = 0; + int expectend = 0; /* Do we expect an end= line? */ char *tmp = NULL, *beginning, *fgstr, *bgstr; colortype *tmpcolor = NULL; @@ -201,113 +215,101 @@ void parse_colors(FILE *rcstream, char *buf, char *ptr) bg = colortoint(bgstr, &bright); /* Now the fun part, start adding regexps to individual strings - in the colorstrings array, woo! */ + in the colorstrings array, woo! */ - if (!strncasecmp(ptr, "start=", 6)) { - ptr += 6; - expectend = 1; - } + while (*ptr != '\0') { - i = 0; - beginning = ptr; - while (*ptr != '\0' && !loopdone) { - switch (*ptr) { - case '\n': - *ptr = ' '; - case ' ': - if (prev != '\\') { - /* This is the end of the regex, uh I guess. - Add it to the colorstrings array for this color */ - - if (i == 0) { - rcfile_error(_("regex length must be > 0")); - continue; - } - - tmp = NULL; - tmp = charalloc(i + 1); - strncpy(tmp, beginning, i); - tmp[i] = '\0'; - - /* Get rid of the leading space */ - ptr = parse_next_word(ptr); - if (ptr == NULL) - return; - - if (colorstrings == NULL) { - colorstrings = nmalloc(sizeof(colortype)); - colorstrings->fg = fg; - colorstrings->bg = bg; - colorstrings->bright = bright; - colorstrings->start = tmp; - colorstrings->next = NULL; - tmpcolor = colorstrings; + while (*ptr == ' ') + ptr++; + + if (*ptr == '\n' || *ptr == '\0') + break; + + if (!strncasecmp(ptr, "start=", 6)) { + ptr += 6; + expectend = 1; + } + + if (*ptr != '"') { + rcfile_error(_("regex strings must begin and end with a \" character\n")); + continue; + } + ptr++; + beginning = ptr; + ptr = parse_next_regex(ptr); + + tmp = NULL; + tmp = mallocstrcpy(tmp, beginning); + + if (colorstrings == NULL) { + colorstrings = nmalloc(sizeof(colortype)); + colorstrings->fg = fg; + colorstrings->bg = bg; + colorstrings->bright = bright; + colorstrings->start = tmp; + colorstrings->next = NULL; + tmpcolor = colorstrings; #ifdef DEBUG - fprintf(stderr, "Starting a new colorstring for fg %d bg %d\n", fg, bg); - fprintf(stderr, "string val=%s\n", tmp); + fprintf(stderr, + "Starting a new colorstring for fg %d bg %d\n", + fg, bg); + fprintf(stderr, "string val=%s\n", tmp); #endif - } else { - for (tmpcolor = colorstrings; tmpcolor->next != NULL; - tmpcolor = tmpcolor->next) - ; + } else { + for (tmpcolor = colorstrings; + tmpcolor->next != NULL; tmpcolor = tmpcolor->next); #ifdef DEBUG - fprintf(stderr, "Adding new entry for fg %d bg %d\n", fg, bg); - fprintf(stderr, "string val=%s\n", tmp); + fprintf(stderr, "Adding new entry for fg %d bg %d\n", fg, bg); + fprintf(stderr, "string val=%s\n", tmp); #endif - tmpcolor->next = nmalloc(sizeof(colortype)); - tmpcolor->next->fg = fg; - tmpcolor->next->bg = bg; - tmpcolor->next->bright = bright; - tmpcolor->next->start = tmp; - tmpcolor->next->next = NULL; - tmpcolor = tmpcolor->next; - } + tmpcolor->next = nmalloc(sizeof(colortype)); + tmpcolor->next->fg = fg; + tmpcolor->next->bg = bg; + tmpcolor->next->bright = bright; + tmpcolor->next->start = tmp; + tmpcolor->next->next = NULL; + tmpcolor = tmpcolor->next; + } - i = 0; - beginning = ptr; + if (expectend) { + if (ptr == NULL || strncasecmp(ptr, "end=", 4)) { + rcfile_error(_ + ("\n\t\"start=\" requires a corresponding \"end=\"")); + return; + } - if (expectend) - loopdone = 1; + ptr += 4; - break; + if (*ptr != '"') { + rcfile_error(_ + ("regex strings must begin and end with a \" character\n")); + continue; } - /* Else drop through to the default case */ - default: - i++; - prev = *ptr; ptr++; - break; - } - } - if (expectend) { - if (ptr == NULL || strncasecmp(ptr, "end=", 4)) { - rcfile_error( - _("\n\t\"start=\" requires a corresponding \"end=\"")); - return; - } - ptr += 4; - beginning = ptr; - ptr = parse_next_word(ptr); + beginning = ptr; + ptr = parse_next_regex(ptr); #ifdef DEBUG - fprintf(stderr, "For end part, beginning = \"%s\"\n", beginning); + fprintf(stderr, "For end part, beginning = \"%s\"\n", + beginning); #endif - tmp = NULL; - tmp = mallocstrcpy(tmp, beginning); - tmpcolor->end = tmp; - - } + tmp = NULL; + tmp = mallocstrcpy(tmp, beginning); + tmpcolor->end = tmp; + } else + tmpcolor->end = NULL; + } } -#endif /* ENABLE_COLOR */ +#endif /* ENABLE_COLOR */ /* Parse the RC file, once it has been opened successfully */ -void parse_rcfile(FILE *rcstream) +void parse_rcfile(FILE * rcstream) { char *buf, *ptr, *keyword, *option; int set = 0, i; @@ -316,8 +318,8 @@ void parse_rcfile(FILE *rcstream) while (fgets(buf, 1023, rcstream) > 0) { lineno++; ptr = buf; - while ((*ptr == ' ' || *ptr == '\t') && - (*ptr != '\n' && *ptr != '\0')) + while ((*ptr == ' ' || *ptr == '\t') && + (*ptr != '\n' && *ptr != '\0')) ptr++; if (*ptr == '\n' || *ptr == '\0') @@ -327,7 +329,7 @@ void parse_rcfile(FILE *rcstream) #ifdef DEBUG fprintf(stderr, _("parse_rcfile: Read a comment\n")); #endif - continue; /* Skip past commented lines */ + continue; /* Skip past commented lines */ } /* Else skip to the next space */ @@ -344,7 +346,7 @@ void parse_rcfile(FILE *rcstream) #ifdef ENABLE_COLOR else if (!strcasecmp(keyword, "color")) parse_colors(rcstream, buf, ptr); -#endif /* ENABLE_COLOR */ +#endif /* ENABLE_COLOR */ else { rcfile_msg(_("command %s not understood"), keyword); continue; @@ -356,28 +358,28 @@ void parse_rcfile(FILE *rcstream) if (set != 0) { for (i = 0; i <= NUM_RCOPTS - 1; i++) { - if (!strcasecmp(option, rcopts[i].name)) { + if (!strcasecmp(option, rcopts[i].name)) { #ifdef DEBUG - fprintf(stderr, _("parse_rcfile: Parsing option %s\n"), - rcopts[i].name); + fprintf(stderr, _("parse_rcfile: Parsing option %s\n"), + rcopts[i].name); #endif if (set == 1 || rcopts[i].flag == FOLLOW_SYMLINKS) { - if ( - !strcasecmp(rcopts[i].name, "operatingdir") || + if (!strcasecmp(rcopts[i].name, "operatingdir") || !strcasecmp(rcopts[i].name, "tabsize") || #ifndef DISABLE_WRAPJUSTIFY - !strcasecmp(rcopts[i].name, "fill") || + !strcasecmp(rcopts[i].name, "fill") || #endif #ifndef DISABLE_SPELLER !strcasecmp(rcopts[i].name, "speller") #else - 0 + 0 #endif - ) { + ) { if (*ptr == '\n' || *ptr == '\0') { - rcfile_error(_("option %s requires an argument"), - rcopts[i].name); + rcfile_error(_ + ("option %s requires an argument"), + rcopts[i].name); continue; } option = ptr; @@ -386,36 +388,42 @@ void parse_rcfile(FILE *rcstream) #ifndef DISABLE_WRAPJUSTIFY if ((i = atoi(option)) < MIN_FILL_LENGTH) { - rcfile_error( - _("requested fill size %d too small"), i); - } - else - fill = i; + rcfile_error(_ + ("requested fill size %d too small"), + i); + } else + fill = i; #endif - } else if (!strcasecmp(rcopts[i].name, "tabsize")) { - if ((i = atoi(option)) <= 0) { - rcfile_error( - _("requested tab size %d too small"), i); - } else { - tabsize = i; - } + } else + if (!strcasecmp(rcopts[i].name, "tabsize")) + { + if ((i = atoi(option)) <= 0) { + rcfile_error(_ + ("requested tab size %d too small"), + i); + } else { + tabsize = i; + } } else { #ifndef DISABLE_SPELLER - alt_speller = charalloc(strlen(option) + 1); - strcpy(alt_speller, option); + alt_speller = + charalloc(strlen(option) + 1); + strcpy(alt_speller, option); #endif } - } else + } else SET(rcopts[i].flag); #ifdef DEBUG - fprintf(stderr, _("set flag %d!\n"), rcopts[i].flag); + fprintf(stderr, _("set flag %d!\n"), + rcopts[i].flag); #endif } else { UNSET(rcopts[i].flag); #ifdef DEBUG - fprintf(stderr, _("unset flag %d!\n"), rcopts[i].flag); + fprintf(stderr, _("unset flag %d!\n"), + rcopts[i].flag); #endif - } + } } } } @@ -448,7 +456,7 @@ void do_rcfile(void) if (errno != ENOENT) rcfile_error(unable, errno); return; - } + } if ((rcstream = fopen(nanorc, "r")) == NULL) { rcfile_error(unable, strerror(errno)); @@ -461,5 +469,4 @@ void do_rcfile(void) } -#endif /* ENABLE_NANORC */ - +#endif /* ENABLE_NANORC */ diff --git a/winio.c b/winio.c index 2e109561..38824d04 100644 --- a/winio.c +++ b/winio.c @@ -774,10 +774,10 @@ void edit_add(filestruct * fileptr, int yval, int start, int virt_cur_x, #ifdef ENABLE_COLOR colortype *tmpcolor = NULL; int k, paintlen; + filestruct *e, *s; + regoff_t ematch, smatch; #endif - - /* Just paint the string in any case (we'll add color or reverse on just the text that needs it */ mvwaddnstr(edit, yval, 0, &fileptr->data[start], @@ -788,44 +788,145 @@ void edit_add(filestruct * fileptr, int yval, int start, int virt_cur_x, for (tmpcolor = colorstrings; tmpcolor != NULL; tmpcolor = tmpcolor->next) { - k = start; - regcomp(&search_regexp, tmpcolor->start, 0); - while (!regexec(&search_regexp, &fileptr->data[k], 1, - regmatches, 0)) { + if (tmpcolor->end == NULL) { - if (regmatches[0].rm_eo - regmatches[0].rm_so < 1) { - statusbar("Refusing 0 length regex match"); - break; - } + /* First, highlight all single-line regexes */ + k = start; + regcomp(&search_regexp, tmpcolor->start, 0); + while (!regexec(&search_regexp, &fileptr->data[k], 1, + regmatches, 0)) { + + if (regmatches[0].rm_eo - regmatches[0].rm_so < 1) { + statusbar("Refusing 0 length regex match"); + break; + } #ifdef DEBUG - fprintf(stderr, "Match! (%d chars) \"%s\"\n", - regmatches[0].rm_eo - regmatches[0].rm_so, - &fileptr->data[k + regmatches[0].rm_so]); + fprintf(stderr, "Match! (%d chars) \"%s\"\n", + regmatches[0].rm_eo - regmatches[0].rm_so, + &fileptr->data[k + regmatches[0].rm_so]); #endif - if (regmatches[0].rm_so < COLS - 1) { - if (tmpcolor->bright) - wattron(edit, A_BOLD); - wattron(edit, COLOR_PAIR(tmpcolor->pairnum)); + if (regmatches[0].rm_so < COLS - 1) { + if (tmpcolor->bright) + wattron(edit, A_BOLD); + wattron(edit, COLOR_PAIR(tmpcolor->pairnum)); + + if (regmatches[0].rm_eo + k <= COLS) + paintlen = + regmatches[0].rm_eo - regmatches[0].rm_so; + else + paintlen = COLS - k - regmatches[0].rm_so - 1; + + mvwaddnstr(edit, yval, regmatches[0].rm_so + k, + &fileptr->data[k + regmatches[0].rm_so], + paintlen); + + } - if (regmatches[0].rm_eo + k <= COLS) - paintlen = - regmatches[0].rm_eo - regmatches[0].rm_so; - else - paintlen = COLS - k - regmatches[0].rm_so - 1; + if (tmpcolor->bright) + wattroff(edit, A_BOLD); + wattroff(edit, COLOR_PAIR(tmpcolor->pairnum)); - mvwaddnstr(edit, yval, regmatches[0].rm_so + k, - &fileptr->data[k + regmatches[0].rm_so], - paintlen); + k += regmatches[0].rm_eo; + } + } + /* Now, if there's an 'end' somewhere below, and a 'start' + somewhere above, things get really fun. We have to look + down for an end, make sure there's not a start before + the end after us, and then look up for a start, + and see if there's an end after the start, before us :) */ + else { + s = fileptr; + while (s != NULL) { + regcomp(&search_regexp, tmpcolor->start, 0); + if (!regexec + (&search_regexp, s->data, 1, regmatches, 0)) + break; + s = s->prev; } - if (tmpcolor->bright) - wattroff(edit, A_BOLD); - wattroff(edit, COLOR_PAIR(tmpcolor->pairnum)); + if (s != NULL) { + /* We found a start, mark it */ + smatch = regmatches[0].rm_so; + + e = s; + while (e != NULL && e != fileptr) { + regcomp(&search_regexp, tmpcolor->end, 0); + if (!regexec + (&search_regexp, e->data, 1, regmatches, 0)) + break; + e = e->next; + } + + if (e != fileptr) + continue; /* There's an end before us */ + else { /* Keep looking for an end */ + while (e != NULL) { + regcomp(&search_regexp, tmpcolor->end, 0); + if (!regexec + (&search_regexp, e->data, 1, regmatches, + 0)) + break; + e = e->next; + } + + if (e == NULL) + continue; /* There's no start before the end :) */ + else { /* Okay, we found an end, mark it! */ + ematch = regmatches[0].rm_eo; + + while (e != NULL) { + regcomp(&search_regexp, tmpcolor->end, 0); + if (!regexec + (&search_regexp, e->data, 1, + regmatches, 0)) + break; + e = e->next; + } + + if (e == NULL) + continue; /* No end, oh well :) */ + + /* Didn't find another end, we must be in the + middle of a highlighted bit */ + + if (tmpcolor->bright) + wattron(edit, A_BOLD); + + wattron(edit, COLOR_PAIR(tmpcolor->pairnum)); + + if (s == fileptr && e == fileptr) + mvwaddnstr(edit, yval, start + smatch, + &fileptr->data[start + smatch], + ematch - smatch); + else if (s == fileptr) + mvwaddnstr(edit, yval, start + smatch, + &fileptr->data[start + smatch], + COLS - smatch); + else if (e == fileptr) + mvwaddnstr(edit, yval, start, + &fileptr->data[start], + ematch - start); + else + mvwaddnstr(edit, yval, start, + &fileptr->data[start], + COLS); + + if (tmpcolor->bright) + wattroff(edit, A_BOLD); + + wattroff(edit, COLOR_PAIR(tmpcolor->pairnum)); + + } + + } + + /* Else go to the next string, yahoo! =) */ + } - k += regmatches[0].rm_eo; } + } #endif /* ENABLE_COLOR */ #ifndef NANO_SMALL -- 2.39.5