summaryrefslogtreecommitdiff
path: root/src/rcfile.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/rcfile.c')
-rw-r--r--src/rcfile.c710
1 files changed, 464 insertions, 246 deletions
diff --git a/src/rcfile.c b/src/rcfile.c
index 5aff6d9..a60ea69 100644
--- a/src/rcfile.c
+++ b/src/rcfile.c
@@ -1,9 +1,9 @@
-/* $Id: rcfile.c 4508 2010-06-21 03:10:10Z astyanax $ */
+/* $Id: rcfile.c 5134 2015-03-08 15:42:52Z bens $ */
/**************************************************************************
* rcfile.c *
* *
- * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 *
- * Free Software Foundation, Inc. *
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, *
+ * 2010, 2011, 2013, 2014 Free Software Foundation, Inc. *
* 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, or (at your option) *
@@ -23,6 +23,7 @@
#include "proto.h"
+#include <glob.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
@@ -30,7 +31,7 @@
#include <unistd.h>
#include <ctype.h>
-#ifdef ENABLE_NANORC
+#ifndef DISABLE_NANORC
static const rcoption rcopts[] = {
{"boldtext", BOLD_TEXT},
@@ -41,10 +42,13 @@ static const rcoption rcopts[] = {
#ifndef DISABLE_WRAPJUSTIFY
{"fill", 0},
#endif
+#ifndef NANO_TINY
+ {"locking", LOCKING},
+#endif
#ifndef DISABLE_MOUSE
{"mouse", USE_MOUSE},
#endif
-#ifdef ENABLE_MULTIBUFFER
+#ifndef DISABLE_MULTIBUFFER
{"multibuffer", MULTIBUFFER},
#endif
{"morespace", MORE_SPACE},
@@ -85,16 +89,22 @@ static const rcoption rcopts[] = {
{"historylog", HISTORYLOG},
{"matchbrackets", 0},
{"noconvert", NO_CONVERT},
+ {"poslog", POS_HISTORY},
{"quiet", QUIET},
{"quickblank", QUICK_BLANK},
{"smarthome", SMART_HOME},
{"smooth", SMOOTH_SCROLL},
{"tabstospaces", TABS_TO_SPACES},
- {"undo", UNDOABLE},
{"whitespace", 0},
{"wordbounds", WORD_BOUNDS},
{"softwrap", SOFTWRAP},
#endif
+#ifndef DISABLE_COLOR
+ {"titlecolor", 0},
+ {"statuscolor", 0},
+ {"keycolor", 0},
+ {"functioncolor", 0},
+#endif
{NULL, 0}
};
@@ -104,14 +114,11 @@ static size_t lineno = 0;
/* If we did, the line number where the last error occurred. */
static char *nanorc = NULL;
/* The path to the rcfile we're parsing. */
-#ifdef ENABLE_COLOR
+#ifndef DISABLE_COLOR
static syntaxtype *endsyntax = NULL;
/* The end of the list of syntaxes. */
-static exttype *endheader = NULL;
- /* End of header list */
static colortype *endcolor = NULL;
/* The end of the color list for the current syntax. */
-
#endif
/* We have an error in some part of the rcfile. Print the error message
@@ -136,7 +143,9 @@ void rcfile_error(const char *msg, ...)
fprintf(stderr, "\n");
}
+#endif /* !DISABLE_NANORC */
+#if !defined(DISABLE_NANORC) || !defined(DISABLE_HISTORIES)
/* Parse the next word from the string, null-terminate it, and return
* a pointer to the first character after the null terminator. The
* returned pointer will point to '\0' if we hit the end of the line. */
@@ -156,7 +165,9 @@ char *parse_next_word(char *ptr)
return ptr;
}
+#endif /* !DISABLE_NANORC || !DISABLE_HISTORIES */
+#ifndef DISABLE_NANORC
/* Parse an argument, with optional quotes, after a keyword that takes
* one. If the next word starts with a ", we say that it ends with the
* last " of the line. Otherwise, we interpret it as usual, so that the
@@ -193,7 +204,7 @@ char *parse_argument(char *ptr)
return ptr;
}
-#ifdef ENABLE_COLOR
+#ifndef DISABLE_COLOR
/* Parse the next regex string from the line at ptr, and return it. */
char *parse_next_regex(char *ptr)
{
@@ -248,8 +259,8 @@ bool nregcomp(const char *regex, int eflags)
void parse_syntax(char *ptr)
{
const char *fileregptr = NULL, *nameptr = NULL;
- syntaxtype *tmpsyntax;
- exttype *endext = NULL;
+ syntaxtype *tmpsyntax, *prev_syntax;
+ regexlisttype *endext = NULL;
/* The end of the extensions list for this syntax. */
assert(ptr != NULL);
@@ -275,15 +286,26 @@ void parse_syntax(char *ptr)
/* Search for a duplicate syntax name. If we find one, free it, so
* that we always use the last syntax with a given name. */
+ prev_syntax = NULL;
for (tmpsyntax = syntaxes; tmpsyntax != NULL;
tmpsyntax = tmpsyntax->next) {
if (strcmp(nameptr, tmpsyntax->desc) == 0) {
- syntaxtype *prev_syntax = tmpsyntax;
+ syntaxtype *old_syntax = tmpsyntax;
+
+ if (endsyntax == tmpsyntax)
+ endsyntax = prev_syntax;
tmpsyntax = tmpsyntax->next;
- free(prev_syntax);
+ if (prev_syntax != NULL)
+ prev_syntax->next = tmpsyntax;
+ else
+ syntaxes = tmpsyntax;
+
+ free(old_syntax->desc);
+ free(old_syntax);
break;
}
+ prev_syntax = tmpsyntax;
}
if (syntaxes == NULL) {
@@ -300,11 +322,13 @@ void parse_syntax(char *ptr)
endsyntax->desc = mallocstrcpy(NULL, nameptr);
endsyntax->color = NULL;
endcolor = NULL;
- endheader = NULL;
endsyntax->extensions = NULL;
endsyntax->headers = NULL;
+ endsyntax->magics = NULL;
endsyntax->next = NULL;
endsyntax->nmultis = 0;
+ endsyntax->linter = NULL;
+ endsyntax->formatter = NULL;
#ifdef DEBUG
fprintf(stderr, "Starting a new syntax type: \"%s\"\n", nameptr);
@@ -324,10 +348,9 @@ void parse_syntax(char *ptr)
return;
}
- /* Now load the extensions into their part of the struct. */
+ /* Now load the extension regexes into their part of the struct. */
while (*ptr != '\0') {
- exttype *newext;
- /* The new extension structure. */
+ regexlisttype *newext;
while (*ptr != '"' && *ptr != '\0')
ptr++;
@@ -342,7 +365,7 @@ void parse_syntax(char *ptr)
if (ptr == NULL)
break;
- newext = (exttype *)nmalloc(sizeof(exttype));
+ newext = (regexlisttype *)nmalloc(sizeof(regexlisttype));
/* Save the extension regex if it's valid. */
if (nregcomp(fileregptr, REG_NOSUB)) {
@@ -359,29 +382,53 @@ void parse_syntax(char *ptr)
free(newext);
}
}
+#endif /* !DISABLE_COLOR */
int check_bad_binding(sc *s)
{
#define BADLISTLEN 1
- int badtypes[BADLISTLEN] = {META};
+ key_type badtypes[BADLISTLEN] = {META};
int badseqs[BADLISTLEN] = { 91 };
int i;
for (i = 0; i < BADLISTLEN; i++)
- if (s->type == badtypes[i] && s->seq == badseqs[i])
+ if (s->type == badtypes[i] && s->seq == badseqs[i])
return 1;
return 0;
}
-void parse_keybinding(char *ptr)
+/* Check whether the given executable function is "universal" (meaning
+ * any horizontal movement or deletion) and thus is present in almost
+ * all menus. */
+bool is_universal(void (*func))
+{
+ if (func == do_left || func == do_right ||
+ func == do_home || func == do_end ||
+#ifndef NANO_TINY
+ func == do_prev_word_void || func == do_next_word_void ||
+#endif
+ func == do_verbatim_input || func == do_cut_text_void ||
+ func == do_delete || func == do_backspace ||
+ func == do_tab || func == do_enter)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/* Bind or unbind a key combo, to or from a function. */
+void parse_binding(char *ptr, bool dobind)
{
char *keyptr = NULL, *keycopy = NULL, *funcptr = NULL, *menuptr = NULL;
- sc *s, *newsc;
- int i, menu;
+ sc *s, *newsc = NULL;
+ int menu;
assert(ptr != NULL);
+#ifdef DEBUG
+ fprintf(stderr, "Starting the rebinding code...\n");
+#endif
+
if (*ptr == '\0') {
rcfile_error(N_("Missing key name"));
return;
@@ -390,206 +437,206 @@ void parse_keybinding(char *ptr)
keyptr = ptr;
ptr = parse_next_word(ptr);
keycopy = mallocstrcpy(NULL, keyptr);
- for (i = 0; i < strlen(keycopy); i++)
- keycopy[i] = toupper(keycopy[i]);
- if (keycopy[0] != 'M' && keycopy[0] != '^' && keycopy[0] != 'F' && keycopy[0] != 'K') {
- rcfile_error(
- N_("keybindings must begin with \"^\", \"M\", or \"F\""));
+ if (strlen(keycopy) < 2) {
+ rcfile_error(N_("Key name is too short"));
return;
}
- funcptr = ptr;
- ptr = parse_next_word(ptr);
-
- if (!strcmp(funcptr, "")) {
- rcfile_error(
- N_("Must specify function to bind key to"));
- return;
+ /* Uppercase only the first two or three characters of the key name. */
+ keycopy[0] = toupper(keycopy[0]);
+ keycopy[1] = toupper(keycopy[1]);
+ if (keycopy[0] == 'M' && keycopy[1] == '-') {
+ if (strlen(keycopy) > 2)
+ keycopy[2] = toupper(keycopy[2]);
+ else {
+ rcfile_error(N_("Key name is too short"));
+ return;
+ }
}
- menuptr = ptr;
- ptr = parse_next_word(ptr);
-
- if (!strcmp(menuptr, "")) {
- rcfile_error(
- /* Note to translators, do not translate the word "all"
- in the sentence below, everything else is fine */
- N_("Must specify menu to bind key to (or \"all\")"));
+ /* Allow the codes for Insert and Delete to be rebound, but apart
+ * from those two only Control, Meta and Function sequences. */
+ if (!strcasecmp(keycopy, "Ins") || !strcasecmp(keycopy, "Del"))
+ keycopy[1] = tolower(keycopy[1]);
+ else if (keycopy[0] != '^' && keycopy[0] != 'M' && keycopy[0] != 'F') {
+ rcfile_error(N_("Key name must begin with \"^\", \"M\", or \"F\""));
return;
}
- menu = strtomenu(menuptr);
- newsc = strtosc(menu, funcptr);
- if (newsc == NULL) {
- rcfile_error(
- N_("Could not map name \"%s\" to a function"), funcptr);
- return;
- }
+ if (dobind) {
+ funcptr = ptr;
+ ptr = parse_next_word(ptr);
- if (menu < 1) {
- rcfile_error(
- N_("Could not map name \"%s\" to a menu"), menuptr);
- return;
+ if (funcptr[0] == '\0') {
+ rcfile_error(N_("Must specify a function to bind the key to"));
+ return;
+ }
}
+ menuptr = ptr;
+ ptr = parse_next_word(ptr);
-#ifdef DEBUG
- fprintf(stderr, "newsc now address %d, menu func assigned = %d, menu = %d\n",
- &newsc, newsc->scfunc, menu);
-#endif
-
-
- newsc->keystr = keycopy;
- newsc->menu = menu;
- newsc->type = strtokeytype(newsc->keystr);
- assign_keyinfo(newsc);
-#ifdef DEBUG
- fprintf(stderr, "s->keystr = \"%s\"\n", newsc->keystr);
- fprintf(stderr, "s->seq = \"%d\"\n", newsc->seq);
-#endif
-
- if (check_bad_binding(newsc)) {
- rcfile_error(
- N_("Sorry, keystr \"%s\" is an illegal binding"), newsc->keystr);
+ if (menuptr[0] == '\0') {
+ /* TRANSLATORS: Do not translate the word "all". */
+ rcfile_error(N_("Must specify a menu (or \"all\") in which to bind/unbind the key"));
return;
}
- /* now let's have some fun. Try and delete the other entries
- we found for the same menu, then make this new new
- beginning */
- for (s = sclist; s != NULL; s = s->next) {
- if (((s->menu & newsc->menu)) && s->seq == newsc->seq) {
- s->menu &= ~newsc->menu;
-#ifdef DEBUG
- fprintf(stderr, "replaced menu entry %d\n", s->menu);
-#endif
+ if (dobind) {
+ newsc = strtosc(funcptr);
+ if (newsc == NULL) {
+ rcfile_error(N_("Cannot map name \"%s\" to a function"), funcptr);
+ return;
}
}
- newsc->next = sclist;
- sclist = newsc;
-}
-
-/* Let user unbind a sequence from a given (or all) menus */
-void parse_unbinding(char *ptr)
-{
- char *keyptr = NULL, *keycopy = NULL, *menuptr = NULL;
- sc *s;
- int i, menu;
- assert(ptr != NULL);
-
- if (*ptr == '\0') {
- rcfile_error(N_("Missing key name"));
+ menu = strtomenu(menuptr);
+ if (menu < 1) {
+ rcfile_error(N_("Cannot map name \"%s\" to a menu"), menuptr);
return;
}
- keyptr = ptr;
- ptr = parse_next_word(ptr);
- keycopy = mallocstrcpy(NULL, keyptr);
- for (i = 0; i < strlen(keycopy); i++)
- keycopy[i] = toupper(keycopy[i]);
-
#ifdef DEBUG
- fprintf(stderr, "Starting unbinding code");
+ if (dobind)
+ fprintf(stderr, "newsc address is now %ld, assigned func = %ld, menu = %x\n",
+ (long)&newsc, (long)newsc->scfunc, menu);
+ else
+ fprintf(stderr, "unbinding \"%s\" from menu %x\n", keycopy, menu);
#endif
- if (keycopy[0] != 'M' && keycopy[0] != '^' && keycopy[0] != 'F' && keycopy[0] != 'K') {
- rcfile_error(
- N_("keybindings must begin with \"^\", \"M\", or \"F\""));
- return;
- }
+ if (dobind) {
+ subnfunc *f;
+ int mask = 0;
- menuptr = ptr;
- ptr = parse_next_word(ptr);
+ /* Tally up the menus where the function exists. */
+ for (f = allfuncs; f != NULL; f = f->next)
+ if (f->scfunc == newsc->scfunc)
+ mask = mask | f->menus;
- if (!strcmp(menuptr, "")) {
- rcfile_error(
- /* Note to translators, do not translate the word "all"
- in the sentence below, everything else is fine */
- N_("Must specify menu to bind key to (or \"all\")"));
- return;
- }
+ /* Handle the special case of the toggles. */
+ if (newsc->scfunc == do_toggle_void)
+ mask = MMAIN;
- menu = strtomenu(menuptr);
- if (menu < 1) {
- rcfile_error(
- N_("Could not map name \"%s\" to a menu"), menuptr);
- return;
- }
+ /* Now limit the given menu to those where the function exists. */
+ if (is_universal(newsc->scfunc))
+ menu = menu & MMOST;
+ else
+ menu = menu & mask;
+ if (!menu) {
+ rcfile_error(N_("Function '%s' does not exist in menu '%s'"), funcptr, menuptr);
+ free(newsc);
+ return;
+ }
+ newsc->keystr = keycopy;
+ newsc->menu = menu;
+ newsc->type = strtokeytype(newsc->keystr);
+ assign_keyinfo(newsc);
#ifdef DEBUG
- fprintf(stderr, "unbinding \"%s\" from menu = %d\n", keycopy, menu);
+ fprintf(stderr, "s->keystr = \"%s\"\n", newsc->keystr);
+ fprintf(stderr, "s->seq = \"%d\"\n", newsc->seq);
#endif
- /* Now find the apropriate entries in the menu to delete */
+ if (check_bad_binding(newsc)) {
+ rcfile_error(N_("Sorry, keystroke \"%s\" may not be rebound"), newsc->keystr);
+ free(newsc);
+ return;
+ }
+ }
+
+ /* Now find and delete any existing same shortcut in the menu(s). */
for (s = sclist; s != NULL; s = s->next) {
- if (((s->menu & menu)) && !strcmp(s->keystr,keycopy)) {
- s->menu &= ~menu;
+ if (((s->menu & menu)) && !strcmp(s->keystr, keycopy)) {
#ifdef DEBUG
- fprintf(stderr, "deleted menu entry %d\n", s->menu);
+ fprintf(stderr, "deleting entry from menu %x\n", s->menu);
#endif
+ s->menu &= ~menu;
}
}
+
+ if (dobind) {
+ /* Add the new shortcut at the start of the list. */
+ newsc->next = sclist;
+ sclist = newsc;
+ }
}
+#ifndef DISABLE_COLOR
/* Read and parse additional syntax files. */
-void parse_include(char *ptr)
+static void _parse_include(char *file)
{
struct stat rcinfo;
FILE *rcstream;
- char *option, *nanorc_save = nanorc, *expanded;
- size_t lineno_save = lineno;
- option = ptr;
- if (*option == '"')
- option++;
- ptr = parse_argument(ptr);
-
- /* Can't get the specified file's full path cause it may screw up
- our cwd depending on the parent dirs' permissions, (see Savannah bug 25297) */
+ /* Can't get the specified file's full path because it may screw up
+ * our cwd depending on the parent directories' permissions (see
+ * Savannah bug #25297). */
/* Don't open directories, character files, or block files. */
- if (stat(option, &rcinfo) != -1) {
+ if (stat(file, &rcinfo) != -1) {
if (S_ISDIR(rcinfo.st_mode) || S_ISCHR(rcinfo.st_mode) ||
S_ISBLK(rcinfo.st_mode)) {
rcfile_error(S_ISDIR(rcinfo.st_mode) ?
_("\"%s\" is a directory") :
- _("\"%s\" is a device file"), option);
+ _("\"%s\" is a device file"), file);
}
}
- expanded = real_dir_from_tilde(option);
-
/* Open the new syntax file. */
- if ((rcstream = fopen(expanded, "rb")) == NULL) {
- rcfile_error(_("Error reading %s: %s"), expanded,
+ if ((rcstream = fopen(file, "rb")) == NULL) {
+ rcfile_error(_("Error reading %s: %s"), file,
strerror(errno));
return;
}
/* Use the name and line number position of the new syntax file
* while parsing it, so we can know where any errors in it are. */
- nanorc = expanded;
+ nanorc = file;
lineno = 0;
#ifdef DEBUG
- fprintf(stderr, "Parsing file \"%s\" (expanded from \"%s\")\n", expanded, option);
+ fprintf(stderr, "Parsing file \"%s\"\n", file);
#endif
parse_rcfile(rcstream
-#ifdef ENABLE_COLOR
+#ifndef DISABLE_COLOR
, TRUE
#endif
);
+}
+
+void parse_include(char *ptr)
+{
+ char *option, *nanorc_save = nanorc, *expanded;
+ size_t lineno_save = lineno, i;
+ glob_t files;
+
+ option = ptr;
+ if (*option == '"')
+ option++;
+ ptr = parse_argument(ptr);
+
+ /* Expand tildes first, then the globs. */
+ expanded = real_dir_from_tilde(option);
+
+ if (glob(expanded, GLOB_ERR|GLOB_NOSORT, NULL, &files) == 0) {
+ for (i = 0; i < files.gl_pathc; ++i)
+ _parse_include(files.gl_pathv[i]);
+ } else {
+ rcfile_error(_("Error expanding %s: %s"), option,
+ strerror(errno));
+ }
+
+ globfree(&files);
+ free(expanded);
/* We're done with the new syntax file. Restore the original
* filename and line number position. */
nanorc = nanorc_save;
lineno = lineno_save;
-
}
/* Return the short value corresponding to the color named in colorname,
@@ -637,7 +684,7 @@ short color_to_short(const char *colorname, bool *bright)
void parse_colors(char *ptr, bool icase)
{
short fg, bg;
- bool bright = FALSE, no_fgcolor = FALSE;
+ bool bright = FALSE;
char *fgstr;
assert(ptr != NULL);
@@ -655,36 +702,8 @@ void parse_colors(char *ptr, bool icase)
fgstr = ptr;
ptr = parse_next_word(ptr);
-
- if (strchr(fgstr, ',') != NULL) {
- char *bgcolorname;
-
- strtok(fgstr, ",");
- bgcolorname = strtok(NULL, ",");
- if (bgcolorname == NULL) {
- /* If we have a background color without a foreground color,
- * parse it properly. */
- bgcolorname = fgstr + 1;
- no_fgcolor = TRUE;
- }
- if (strncasecmp(bgcolorname, "bright", 6) == 0) {
- rcfile_error(
- N_("Background color \"%s\" cannot be bright"),
- bgcolorname);
- return;
- }
- bg = color_to_short(bgcolorname, &bright);
- } else
- bg = -1;
-
- if (!no_fgcolor) {
- fg = color_to_short(fgstr, &bright);
-
- /* Don't try to parse screwed-up foreground colors. */
- if (fg == -1)
- return;
- } else
- fg = -1;
+ if (!parse_color_names(fgstr, &fg, &bg, &bright))
+ return;
if (*ptr == '\0') {
rcfile_error(N_("Missing regex string"));
@@ -695,7 +714,7 @@ void parse_colors(char *ptr, bool icase)
* in the colorstrings array, woo! */
while (ptr != NULL && *ptr != '\0') {
colortype *newcolor;
- /* The new color structure. */
+ /* The container for a color plus its regexes. */
bool cancelled = FALSE;
/* The start expression was bad. */
bool expectend = FALSE;
@@ -747,6 +766,10 @@ void parse_colors(char *ptr, bool icase)
#ifdef DEBUG
fprintf(stderr, "Adding new entry for fg %hd, bg %hd\n", fg, bg);
#endif
+ /* Need to recompute endcolor now so we can extend
+ * colors to syntaxes. */
+ for (endcolor = endsyntax->color; endcolor->next != NULL; endcolor = endcolor->next)
+ ;
endcolor->next = newcolor;
}
@@ -776,8 +799,8 @@ void parse_colors(char *ptr, bool icase)
if (ptr == NULL)
break;
- /* If the start regex was invalid, skip past the end regex to
- * stay in sync. */
+ /* If the start regex was invalid, skip past the end regex
+ * to stay in sync. */
if (cancelled)
continue;
@@ -785,17 +808,55 @@ void parse_colors(char *ptr, bool icase)
newcolor->end_regex = (nregcomp(fgstr, icase ? REG_ICASE :
0)) ? mallocstrcpy(NULL, fgstr) : NULL;
- /* Lame way to skip another static counter */
- newcolor->id = endsyntax->nmultis;
- endsyntax->nmultis++;
+ /* Lame way to skip another static counter. */
+ newcolor->id = endsyntax->nmultis;
+ endsyntax->nmultis++;
}
}
}
-/* Parse the headers (1st line) of the file which may influence the regex used. */
-void parse_headers(char *ptr)
+/* Parse the color name, or pair of color names, in combostr. */
+bool parse_color_names(char *combostr, short *fg, short *bg, bool *bright)
{
- char *regstr;
+ bool no_fgcolor = FALSE;
+
+ if (combostr == NULL)
+ return FALSE;
+
+ if (strchr(combostr, ',') != NULL) {
+ char *bgcolorname;
+ strtok(combostr, ",");
+ bgcolorname = strtok(NULL, ",");
+ if (bgcolorname == NULL) {
+ /* If we have a background color without a foreground color,
+ * parse it properly. */
+ bgcolorname = combostr + 1;
+ no_fgcolor = TRUE;
+ }
+ if (strncasecmp(bgcolorname, "bright", 6) == 0) {
+ rcfile_error(N_("Background color \"%s\" cannot be bright"), bgcolorname);
+ return FALSE;
+ }
+ *bg = color_to_short(bgcolorname, bright);
+ } else
+ *bg = -1;
+
+ if (!no_fgcolor) {
+ *fg = color_to_short(combostr, bright);
+
+ /* Don't try to parse screwed-up foreground colors. */
+ if (*fg == -1)
+ return FALSE;
+ } else
+ *fg = -1;
+
+ return TRUE;
+}
+
+/* Parse the header-line regexes that may influence the choice of syntax. */
+void parse_header_exp(char *ptr)
+{
+ regexlisttype *endheader = NULL;
assert(ptr != NULL);
@@ -810,11 +871,9 @@ void parse_headers(char *ptr)
return;
}
- /* Now for the fun part. Start adding regexes to individual strings
- * in the colorstrings array, woo! */
- while (ptr != NULL && *ptr != '\0') {
- exttype *newheader;
- /* The new color structure. */
+ while (*ptr != '\0') {
+ const char *regexstring;
+ regexlisttype *newheader;
if (*ptr != '"') {
rcfile_error(
@@ -825,60 +884,173 @@ void parse_headers(char *ptr)
ptr++;
- regstr = ptr;
+ regexstring = ptr;
ptr = parse_next_regex(ptr);
if (ptr == NULL)
break;
- newheader = (exttype *)nmalloc(sizeof(exttype));
+ newheader = (regexlisttype *)nmalloc(sizeof(regexlisttype));
- /* Save the regex string if it's valid */
- if (nregcomp(regstr, 0)) {
- newheader->ext_regex = mallocstrcpy(NULL, regstr);
+ /* Save the regex string if it's valid. */
+ if (nregcomp(regexstring, 0)) {
+ newheader->ext_regex = mallocstrcpy(NULL, regexstring);
newheader->ext = NULL;
- newheader->next = NULL;
-
-#ifdef DEBUG
- fprintf(stderr, "Starting a new header entry: %s\n", newheader->ext_regex);
-#endif
- if (endheader == NULL) {
+ if (endheader == NULL)
endsyntax->headers = newheader;
- } else {
+ else
endheader->next = newheader;
- }
-
endheader = newheader;
+ endheader->next = NULL;
} else
free(newheader);
+ }
+}
+
+#ifndef DISABLE_COLOR
+/* Parse the magic regexes that may influence the choice of syntax. */
+void parse_magic_exp(char *ptr)
+{
+#ifdef HAVE_LIBMAGIC
+ regexlisttype *endmagic = NULL;
+ assert(ptr != NULL);
+
+ if (syntaxes == NULL) {
+ rcfile_error(
+ N_("Cannot add a magic string regex without a syntax command"));
+ return;
+ }
+
+ if (*ptr == '\0') {
+ rcfile_error(N_("Missing magic string name"));
+ return;
}
+
+ if (*ptr != '"') {
+ rcfile_error(
+ N_("Regex strings must begin and end with a \" character"));
+ return;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "Starting a magic type: \"%s\"\n", ptr);
+#endif
+
+ /* Now load the magic regexes into their part of the struct. */
+ while (*ptr != '\0') {
+ const char *regexstring;
+ regexlisttype *newmagic;
+
+ while (*ptr != '"' && *ptr != '\0')
+ ptr++;
+
+ if (*ptr == '\0')
+ return;
+
+ ptr++;
+
+ regexstring = ptr;
+ ptr = parse_next_regex(ptr);
+ if (ptr == NULL)
+ break;
+
+ newmagic = (regexlisttype *)nmalloc(sizeof(regexlisttype));
+
+ /* Save the regex string if it's valid. */
+ if (nregcomp(regexstring, REG_NOSUB)) {
+ newmagic->ext_regex = mallocstrcpy(NULL, regexstring);
+ newmagic->ext = NULL;
+
+ if (endmagic == NULL)
+ endsyntax->magics = newmagic;
+ else
+ endmagic->next = newmagic;
+ endmagic = newmagic;
+ endmagic->next = NULL;
+ } else
+ free(newmagic);
+ }
+#endif /* HAVE_LIBMAGIC */
}
-#endif /* ENABLE_COLOR */
+#endif /* !DISABLE_COLOR */
+
+/* Parse the linter requested for this syntax. Simple? */
+void parse_linter(char *ptr)
+{
+ assert(ptr != NULL);
+
+ if (syntaxes == NULL) {
+ rcfile_error(
+ N_("Cannot add a linter without a syntax command"));
+ return;
+ }
+
+ if (*ptr == '\0') {
+ rcfile_error(N_("Missing linter command"));
+ return;
+ }
+
+ if (endsyntax->linter != NULL)
+ free(endsyntax->linter);
+
+ /* Let them unset the linter by using "". */
+ if (!strcmp(ptr, "\"\""))
+ endsyntax->linter = NULL;
+ else
+ endsyntax->linter = mallocstrcpy(syntaxes->linter, ptr);
+}
+
+void parse_formatter(char *ptr)
+{
+ assert(ptr != NULL);
+
+ if (syntaxes == NULL) {
+ rcfile_error(
+ N_("Cannot add formatter without a syntax command"));
+ return;
+ }
+
+ if (*ptr == '\0') {
+ rcfile_error(N_("Missing formatter command"));
+ return;
+ }
+
+ if (endsyntax->formatter != NULL)
+ free(endsyntax->formatter);
+
+ /* Let them unset the formatter by using "". */
+ if (!strcmp(ptr, "\"\""))
+ endsyntax->formatter = NULL;
+ else
+ endsyntax->formatter = mallocstrcpy(syntaxes->formatter, ptr);
+}
+#endif /* !DISABLE_COLOR */
/* Check whether the user has unmapped every shortcut for a
-sequence we consider 'vital', like the exit function */
+ * sequence we consider 'vital', like the exit function. */
static void check_vitals_mapped(void)
{
subnfunc *f;
int v;
#define VITALS 5
- short vitals[VITALS] = { DO_EXIT, DO_EXIT, CANCEL_MSG, CANCEL_MSG, CANCEL_MSG };
+ void (*vitals[VITALS])(void) = { do_exit, do_exit, do_cancel, do_cancel, do_cancel };
int inmenus[VITALS] = { MMAIN, MHELP, MWHEREIS, MREPLACE, MGOTOLINE };
for (v = 0; v < VITALS; v++) {
- for (f = allfuncs; f != NULL; f = f->next) {
- if (f->scfunc == vitals[v] && f->menus & inmenus[v]) {
- const sc *s = first_sc_for(inmenus[v], f->scfunc);
- if (!s) {
- rcfile_error(N_("Fatal error: no keys mapped for function \"%s\""),
- f->desc);
- fprintf(stderr, N_("Exiting. Please use nano with the -I option if needed to adjust your nanorc settings\n"));
- exit(1);
- }
- break;
- }
- }
+ for (f = allfuncs; f != NULL; f = f->next) {
+ if (f->scfunc == vitals[v] && f->menus & inmenus[v]) {
+ const sc *s = first_sc_for(inmenus[v], f->scfunc);
+ if (!s) {
+ fprintf(stderr, _("Fatal error: no keys mapped for function "
+ "\"%s\". Exiting.\n"), f->desc);
+ fprintf(stderr, _("If needed, use nano with the -I option "
+ "to adjust your nanorc settings.\n"));
+ exit(1);
+ }
+ break;
+ }
+ }
}
}
@@ -886,7 +1058,7 @@ static void check_vitals_mapped(void)
* and close it afterwards. If syntax_only is TRUE, only allow the file
* to contain color syntax commands: syntax, color, and icolor. */
void parse_rcfile(FILE *rcstream
-#ifdef ENABLE_COLOR
+#ifndef DISABLE_COLOR
, bool syntax_only
#endif
)
@@ -894,6 +1066,9 @@ void parse_rcfile(FILE *rcstream
char *buf = NULL;
ssize_t len;
size_t n = 0;
+#ifndef DISABLE_COLOR
+ syntaxtype *end_syn_save = NULL;
+#endif
while ((len = getline(&buf, &n, rcstream)) > 0) {
char *ptr, *keyword, *option;
@@ -918,9 +1093,32 @@ void parse_rcfile(FILE *rcstream
keyword = ptr;
ptr = parse_next_word(ptr);
+#ifndef DISABLE_COLOR
+ /* Handle extending first... */
+ if (strcasecmp(keyword, "extendsyntax") == 0) {
+ char *syntaxname = ptr;
+ syntaxtype *ts = NULL;
+
+ ptr = parse_next_word(ptr);
+ for (ts = syntaxes; ts != NULL; ts = ts->next)
+ if (!strcmp(ts->desc, syntaxname))
+ break;
+
+ if (ts == NULL) {
+ rcfile_error(N_("Could not find syntax \"%s\" to extend"), syntaxname);
+ continue;
+ } else {
+ end_syn_save = endsyntax;
+ endsyntax = ts;
+ keyword = ptr;
+ ptr = parse_next_word(ptr);
+ }
+ }
+#endif
+
/* Try to parse the keyword. */
if (strcasecmp(keyword, "set") == 0) {
-#ifdef ENABLE_COLOR
+#ifndef DISABLE_COLOR
if (syntax_only)
rcfile_error(
N_("Command \"%s\" not allowed in included file"),
@@ -929,7 +1127,7 @@ void parse_rcfile(FILE *rcstream
#endif
set = 1;
} else if (strcasecmp(keyword, "unset") == 0) {
-#ifdef ENABLE_COLOR
+#ifndef DISABLE_COLOR
if (syntax_only)
rcfile_error(
N_("Command \"%s\" not allowed in included file"),
@@ -938,7 +1136,7 @@ void parse_rcfile(FILE *rcstream
#endif
set = -1;
}
-#ifdef ENABLE_COLOR
+#ifndef DISABLE_COLOR
else if (strcasecmp(keyword, "include") == 0) {
if (syntax_only)
rcfile_error(
@@ -951,25 +1149,41 @@ void parse_rcfile(FILE *rcstream
rcfile_error(N_("Syntax \"%s\" has no color commands"),
endsyntax->desc);
parse_syntax(ptr);
- } else if (strcasecmp(keyword, "header") == 0)
- parse_headers(ptr);
+ }
+ else if (strcasecmp(keyword, "magic") == 0)
+ parse_magic_exp(ptr);
+ else if (strcasecmp(keyword, "header") == 0)
+ parse_header_exp(ptr);
else if (strcasecmp(keyword, "color") == 0)
parse_colors(ptr, FALSE);
else if (strcasecmp(keyword, "icolor") == 0)
parse_colors(ptr, TRUE);
+ else if (strcasecmp(keyword, "linter") == 0)
+ parse_linter(ptr);
+ else if (strcasecmp(keyword, "formatter") == 0)
+ parse_formatter(ptr);
+#endif /* !DISABLE_COLOR */
else if (strcasecmp(keyword, "bind") == 0)
- parse_keybinding(ptr);
+ parse_binding(ptr, TRUE);
else if (strcasecmp(keyword, "unbind") == 0)
- parse_unbinding(ptr);
-#endif /* ENABLE_COLOR */
+ parse_binding(ptr, FALSE);
else
rcfile_error(N_("Command \"%s\" not understood"), keyword);
+#ifndef DISABLE_COLOR
+ /* If we temporarily reset endsyntax to allow extending,
+ * restore the value here. */
+ if (end_syn_save != NULL) {
+ endsyntax = end_syn_save;
+ end_syn_save = NULL;
+ }
+#endif
+
if (set == 0)
continue;
if (*ptr == '\0') {
- rcfile_error(N_("Missing flag"));
+ rcfile_error(N_("Missing option"));
continue;
}
@@ -1013,6 +1227,17 @@ void parse_rcfile(FILE *rcstream
break;
}
+#ifndef DISABLE_COLOR
+ if (strcasecmp(rcopts[i].name, "titlecolor") == 0)
+ specified_color_combo[TITLE_BAR] = option;
+ else if (strcasecmp(rcopts[i].name, "statuscolor") == 0)
+ specified_color_combo[STATUS_BAR] = option;
+ else if (strcasecmp(rcopts[i].name, "keycolor") == 0)
+ specified_color_combo[KEY_COMBO] = option;
+ else if (strcasecmp(rcopts[i].name, "functioncolor") == 0)
+ specified_color_combo[FUNCTION_TAG] = option;
+ else
+#endif
#ifndef DISABLE_OPERATINGDIR
if (strcasecmp(rcopts[i].name, "operatingdir") == 0)
operating_dir = option;
@@ -1111,19 +1336,16 @@ void parse_rcfile(FILE *rcstream
} else if (rcopts[i].flag != 0)
UNSET(rcopts[i].flag);
else
- rcfile_error(N_("Cannot unset flag \"%s\""),
+ rcfile_error(N_("Cannot unset option \"%s\""),
rcopts[i].name);
- /* Looks like we still need this specific hack for undo */
- if (strcasecmp(rcopts[i].name, "undo") == 0)
- shortcut_init(0);
break;
}
}
if (rcopts[i].name == NULL)
- rcfile_error(N_("Unknown flag \"%s\""), option);
+ rcfile_error(N_("Unknown option \"%s\""), option);
}
-#ifdef ENABLE_COLOR
+#ifndef DISABLE_COLOR
if (endsyntax != NULL && endcolor == NULL)
rcfile_error(N_("Syntax \"%s\" has no color commands"),
endsyntax->desc);
@@ -1163,7 +1385,7 @@ void do_rcfile(void)
rcstream = fopen(nanorc, "rb");
if (rcstream != NULL)
parse_rcfile(rcstream
-#ifdef ENABLE_COLOR
+#ifndef DISABLE_COLOR
, FALSE
#endif
);
@@ -1205,7 +1427,7 @@ void do_rcfile(void)
strerror(errno));
} else
parse_rcfile(rcstream
-#ifdef ENABLE_COLOR
+#ifndef DISABLE_COLOR
, FALSE
#endif
);
@@ -1221,10 +1443,6 @@ void do_rcfile(void)
while (getchar() != '\n')
;
}
-
-#ifdef ENABLE_COLOR
- set_colorpairs();
-#endif
}
-#endif /* ENABLE_NANORC */
+#endif /* !DISABLE_NANORC */