2001-04-18 04:28:54 +00:00
|
|
|
/**************************************************************************
|
2016-08-29 15:10:49 +00:00
|
|
|
* rcfile.c -- This file is part of GNU nano. *
|
2001-04-18 04:28:54 +00:00
|
|
|
* *
|
2019-02-24 18:32:17 +00:00
|
|
|
* Copyright (C) 2001-2011, 2013-2019 Free Software Foundation, Inc. *
|
2016-08-29 13:14:18 +00:00
|
|
|
* Copyright (C) 2014 Mike Frysinger *
|
2019-03-10 15:29:57 +00:00
|
|
|
* Copyright (C) 2014-2019 Benno Schulenberg *
|
2016-08-29 13:14:18 +00:00
|
|
|
* *
|
2016-08-29 15:10:49 +00:00
|
|
|
* GNU nano is free software: you can redistribute it and/or modify *
|
|
|
|
* it under the terms of the GNU General Public License as published *
|
|
|
|
* by the Free Software Foundation, either version 3 of the License, *
|
|
|
|
* or (at your option) any later version. *
|
2001-04-18 04:28:54 +00:00
|
|
|
* *
|
2016-08-29 15:10:49 +00:00
|
|
|
* GNU nano is distributed in the hope that it will be useful, *
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty *
|
|
|
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
|
|
|
|
* See the GNU General Public License for more details. *
|
2001-04-18 04:28:54 +00:00
|
|
|
* *
|
|
|
|
* You should have received a copy of the GNU General Public License *
|
2016-08-29 15:10:49 +00:00
|
|
|
* along with this program. If not, see http://www.gnu.org/licenses/. *
|
2001-04-18 04:28:54 +00:00
|
|
|
* *
|
|
|
|
**************************************************************************/
|
|
|
|
|
2005-12-08 02:47:10 +00:00
|
|
|
#include "proto.h"
|
2002-09-13 18:14:04 +00:00
|
|
|
|
2017-08-06 11:32:44 +00:00
|
|
|
#include <ctype.h>
|
|
|
|
#include <errno.h>
|
2014-03-30 20:37:40 +00:00
|
|
|
#include <glob.h>
|
2001-04-18 04:28:54 +00:00
|
|
|
#include <string.h>
|
2002-05-11 03:04:44 +00:00
|
|
|
#include <unistd.h>
|
2001-04-18 04:28:54 +00:00
|
|
|
|
2017-05-08 17:42:44 +00:00
|
|
|
#ifdef ENABLE_NANORC
|
2001-04-18 04:28:54 +00:00
|
|
|
|
2016-11-27 15:34:34 +00:00
|
|
|
#ifndef RCFILE_NAME
|
2018-04-20 08:47:10 +00:00
|
|
|
#define HOME_RC_NAME ".nanorc"
|
2017-10-27 21:15:06 +00:00
|
|
|
#define RCFILE_NAME "nanorc"
|
2018-04-20 08:47:10 +00:00
|
|
|
#else
|
|
|
|
#define HOME_RC_NAME RCFILE_NAME
|
2016-11-27 15:34:34 +00:00
|
|
|
#endif
|
|
|
|
|
2006-08-21 21:37:39 +00:00
|
|
|
static const rcoption rcopts[] = {
|
2017-12-29 18:27:33 +00:00
|
|
|
{"boldtext", BOLD_TEXT},
|
2017-10-31 16:34:07 +00:00
|
|
|
#ifdef ENABLE_JUSTIFY
|
2017-12-29 18:27:33 +00:00
|
|
|
{"brackets", 0},
|
2019-01-28 18:45:12 +00:00
|
|
|
#endif
|
|
|
|
#ifdef ENABLE_WRAPPING
|
|
|
|
{"breaklonglines", BREAK_LONG_LINES},
|
2002-06-28 22:45:14 +00:00
|
|
|
#endif
|
2017-12-29 18:27:33 +00:00
|
|
|
{"constantshow", CONSTANT_SHOW},
|
2019-01-28 18:45:12 +00:00
|
|
|
{"emptyline", EMPTY_LINE},
|
2017-10-31 16:39:30 +00:00
|
|
|
#ifdef ENABLED_WRAPORJUSTIFY
|
2017-12-29 18:27:33 +00:00
|
|
|
{"fill", 0},
|
2002-07-19 01:08:59 +00:00
|
|
|
#endif
|
2017-10-29 18:42:12 +00:00
|
|
|
#ifdef ENABLE_HISTORIES
|
2017-12-29 18:27:33 +00:00
|
|
|
{"historylog", HISTORYLOG},
|
2018-05-13 07:47:40 +00:00
|
|
|
#endif
|
2019-01-28 18:45:12 +00:00
|
|
|
{"jumpyscrolling", JUMPY_SCROLLING},
|
2018-05-13 07:47:40 +00:00
|
|
|
#ifdef ENABLE_LINENUMBERS
|
|
|
|
{"linenumbers", LINE_NUMBERS},
|
2013-01-01 03:24:39 +00:00
|
|
|
#endif
|
2019-03-10 16:05:19 +00:00
|
|
|
{"morespace", MORE_SPACE}, /* Deprecated; remove in 2021. */
|
2017-05-01 18:45:07 +00:00
|
|
|
#ifdef ENABLE_MOUSE
|
2017-12-29 18:27:33 +00:00
|
|
|
{"mouse", USE_MOUSE},
|
2002-06-28 22:45:14 +00:00
|
|
|
#endif
|
2017-05-01 18:20:34 +00:00
|
|
|
#ifdef ENABLE_MULTIBUFFER
|
2017-12-29 18:27:33 +00:00
|
|
|
{"multibuffer", MULTIBUFFER},
|
2002-06-28 22:45:14 +00:00
|
|
|
#endif
|
2017-12-29 18:27:33 +00:00
|
|
|
{"nohelp", NO_HELP},
|
2019-04-07 07:01:52 +00:00
|
|
|
{"nonewlines", NO_NEWLINES},
|
2017-12-29 18:27:33 +00:00
|
|
|
{"nopauses", NO_PAUSES},
|
2017-10-29 20:00:09 +00:00
|
|
|
#ifdef ENABLE_WRAPPING
|
2019-03-10 16:05:19 +00:00
|
|
|
{"nowrap", NO_WRAP}, /* Deprecated; remove in 2021. */
|
2002-07-19 01:08:59 +00:00
|
|
|
#endif
|
2017-10-29 20:08:07 +00:00
|
|
|
#ifdef ENABLE_OPERATINGDIR
|
2017-12-29 18:27:33 +00:00
|
|
|
{"operatingdir", 0},
|
2015-06-18 18:51:27 +00:00
|
|
|
#endif
|
2017-10-29 18:42:12 +00:00
|
|
|
#ifdef ENABLE_HISTORIES
|
2018-09-30 11:27:08 +00:00
|
|
|
{"positionlog", POSITIONLOG},
|
2002-06-28 22:45:14 +00:00
|
|
|
#endif
|
2017-12-29 18:27:33 +00:00
|
|
|
{"preserve", PRESERVE},
|
2017-10-31 16:34:07 +00:00
|
|
|
#ifdef ENABLE_JUSTIFY
|
2017-12-29 18:27:33 +00:00
|
|
|
{"punct", 0},
|
|
|
|
{"quotestr", 0},
|
2002-06-28 22:45:14 +00:00
|
|
|
#endif
|
2017-12-29 18:27:33 +00:00
|
|
|
{"quickblank", QUICK_BLANK},
|
2018-12-31 17:34:28 +00:00
|
|
|
{"rawsequences", RAW_SEQUENCES},
|
2017-12-29 18:27:33 +00:00
|
|
|
{"rebinddelete", REBIND_DELETE},
|
|
|
|
{"regexp", USE_REGEXP},
|
2017-10-31 18:32:42 +00:00
|
|
|
#ifdef ENABLE_SPELLER
|
2017-12-29 18:27:33 +00:00
|
|
|
{"speller", 0},
|
2002-06-28 22:45:14 +00:00
|
|
|
#endif
|
2017-12-29 18:27:33 +00:00
|
|
|
{"suspend", SUSPEND},
|
|
|
|
{"tabsize", 0},
|
|
|
|
{"tempfile", TEMP_FILE},
|
|
|
|
{"view", VIEW_MODE},
|
2005-11-15 03:17:35 +00:00
|
|
|
#ifndef NANO_TINY
|
2018-04-30 20:10:27 +00:00
|
|
|
{"afterends", AFTER_ENDS},
|
2017-12-29 18:27:33 +00:00
|
|
|
{"allow_insecure_backup", INSECURE_BACKUP},
|
|
|
|
{"atblanks", AT_BLANKS},
|
|
|
|
{"autoindent", AUTOINDENT},
|
|
|
|
{"backup", BACKUP_FILE},
|
|
|
|
{"backupdir", 0},
|
|
|
|
{"casesensitive", CASE_SENSITIVE},
|
|
|
|
{"cutfromcursor", CUT_FROM_CURSOR},
|
2018-12-17 18:57:30 +00:00
|
|
|
{"guidestripe", 0},
|
2017-12-29 18:27:33 +00:00
|
|
|
{"locking", LOCKING},
|
|
|
|
{"matchbrackets", 0},
|
|
|
|
{"noconvert", NO_CONVERT},
|
|
|
|
{"showcursor", SHOW_CURSOR},
|
|
|
|
{"smarthome", SMART_HOME},
|
2019-03-10 16:05:19 +00:00
|
|
|
{"smooth", SMOOTH_SCROLL}, /* Deprecated; remove in 2021. */
|
2017-12-29 18:27:33 +00:00
|
|
|
{"softwrap", SOFTWRAP},
|
|
|
|
{"tabstospaces", TABS_TO_SPACES},
|
|
|
|
{"trimblanks", TRIM_BLANKS},
|
|
|
|
{"unix", MAKE_IT_UNIX},
|
|
|
|
{"whitespace", 0},
|
|
|
|
{"wordbounds", WORD_BOUNDS},
|
|
|
|
{"wordchars", 0},
|
2018-10-24 09:02:08 +00:00
|
|
|
{"zap", LET_THEM_ZAP},
|
2014-05-03 18:24:45 +00:00
|
|
|
#endif
|
2017-11-01 18:45:33 +00:00
|
|
|
#ifdef ENABLE_COLOR
|
2017-12-29 18:27:33 +00:00
|
|
|
{"titlecolor", 0},
|
|
|
|
{"numbercolor", 0},
|
2019-03-03 10:04:19 +00:00
|
|
|
{"stripecolor", 0},
|
2017-12-29 18:27:33 +00:00
|
|
|
{"selectedcolor", 0},
|
|
|
|
{"statuscolor", 0},
|
2018-02-20 08:50:54 +00:00
|
|
|
{"errorcolor", 0},
|
2017-12-29 18:27:33 +00:00
|
|
|
{"keycolor", 0},
|
|
|
|
{"functioncolor", 0},
|
2004-05-29 16:25:30 +00:00
|
|
|
#endif
|
2017-12-29 18:27:33 +00:00
|
|
|
{NULL, 0}
|
2002-01-19 16:52:34 +00:00
|
|
|
};
|
2001-04-18 04:28:54 +00:00
|
|
|
|
2005-06-28 06:25:34 +00:00
|
|
|
static size_t lineno = 0;
|
2017-12-29 18:27:33 +00:00
|
|
|
/* The line number of the last encountered error. */
|
2005-03-10 20:55:11 +00:00
|
|
|
static char *nanorc = NULL;
|
2017-12-29 18:27:33 +00:00
|
|
|
/* The path to the rcfile we're parsing. */
|
2017-11-01 18:45:33 +00:00
|
|
|
#ifdef ENABLE_COLOR
|
2016-02-28 20:38:14 +00:00
|
|
|
static bool opensyntax = FALSE;
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Whether we're allowed to add to the last syntax. When a file ends,
|
|
|
|
* or when a new syntax command is seen, this bool becomes FALSE. */
|
2016-03-10 20:36:12 +00:00
|
|
|
static syntaxtype *live_syntax;
|
2017-12-29 18:27:33 +00:00
|
|
|
/* The syntax that is currently being parsed. */
|
2016-03-13 20:05:36 +00:00
|
|
|
static colortype *lastcolor = NULL;
|
2017-12-29 18:27:33 +00:00
|
|
|
/* The end of the color list for the current syntax. */
|
2005-03-10 20:55:11 +00:00
|
|
|
#endif
|
2002-01-18 21:54:35 +00:00
|
|
|
|
2017-10-07 14:03:22 +00:00
|
|
|
/* Report an error in an rcfile, printing it to stderr. */
|
2002-08-21 16:10:37 +00:00
|
|
|
void rcfile_error(const char *msg, ...)
|
2001-04-18 04:28:54 +00:00
|
|
|
{
|
2017-12-29 18:27:33 +00:00
|
|
|
va_list ap;
|
2001-04-18 04:28:54 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (rcfile_with_errors == NULL)
|
|
|
|
rcfile_with_errors = strdup(nanorc);
|
2017-10-28 12:18:37 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (lineno > 0)
|
|
|
|
fprintf(stderr, _("Error in %s on line %zu: "), nanorc, lineno);
|
2002-03-29 19:41:57 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
va_start(ap, msg);
|
|
|
|
vfprintf(stderr, _(msg), ap);
|
|
|
|
va_end(ap);
|
2004-08-11 05:13:08 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
fprintf(stderr, "\n");
|
2001-04-18 04:28:54 +00:00
|
|
|
}
|
2017-05-08 17:42:44 +00:00
|
|
|
#endif /* ENABLE_NANORC */
|
2001-04-18 04:28:54 +00:00
|
|
|
|
2017-10-29 18:42:12 +00:00
|
|
|
#if defined(ENABLE_NANORC) || defined(ENABLE_HISTORIES)
|
2006-08-21 21:37:39 +00:00
|
|
|
/* 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. */
|
2002-08-21 16:10:37 +00:00
|
|
|
char *parse_next_word(char *ptr)
|
2001-04-18 04:28:54 +00:00
|
|
|
{
|
2017-12-29 18:27:33 +00:00
|
|
|
while (!isblank((unsigned char)*ptr) && *ptr != '\0')
|
|
|
|
ptr++;
|
2002-01-19 16:52:34 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (*ptr == '\0')
|
|
|
|
return ptr;
|
2002-01-19 16:52:34 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Null-terminate and advance ptr. */
|
|
|
|
*ptr++ = '\0';
|
2002-01-19 16:52:34 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
while (isblank((unsigned char)*ptr))
|
|
|
|
ptr++;
|
2002-01-19 16:52:34 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
return ptr;
|
2002-01-19 16:52:34 +00:00
|
|
|
}
|
2017-10-29 18:42:12 +00:00
|
|
|
#endif /* ENABLE_NANORC || ENABLE_HISTORIES */
|
2002-01-18 21:54:35 +00:00
|
|
|
|
2017-05-08 17:42:44 +00:00
|
|
|
#ifdef ENABLE_NANORC
|
2006-01-06 21:51:10 +00:00
|
|
|
/* 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
|
|
|
|
* arguments can contain "'s too. */
|
2002-08-21 16:10:37 +00:00
|
|
|
char *parse_argument(char *ptr)
|
|
|
|
{
|
2017-12-29 18:27:33 +00:00
|
|
|
const char *ptr_save = ptr;
|
|
|
|
char *last_quote = NULL;
|
|
|
|
|
|
|
|
if (*ptr != '"')
|
|
|
|
return parse_next_word(ptr);
|
|
|
|
|
2018-01-19 17:05:24 +00:00
|
|
|
while (*ptr != '\0') {
|
|
|
|
if (*++ptr == '"')
|
2017-12-29 18:27:33 +00:00
|
|
|
last_quote = ptr;
|
2018-01-19 17:05:24 +00:00
|
|
|
}
|
2017-12-29 18:27:33 +00:00
|
|
|
|
|
|
|
if (last_quote == NULL) {
|
|
|
|
rcfile_error(N_("Argument '%s' has an unterminated \""), ptr_save);
|
2018-01-19 17:05:24 +00:00
|
|
|
return NULL;
|
2017-12-29 18:27:33 +00:00
|
|
|
}
|
2018-01-19 17:05:24 +00:00
|
|
|
|
|
|
|
*last_quote = '\0';
|
|
|
|
ptr = last_quote + 1;
|
|
|
|
|
|
|
|
while (isblank((unsigned char)*ptr))
|
|
|
|
ptr++;
|
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
return ptr;
|
2002-07-19 01:08:59 +00:00
|
|
|
}
|
|
|
|
|
2017-11-01 18:45:33 +00:00
|
|
|
#ifdef ENABLE_COLOR
|
2016-03-11 16:39:27 +00:00
|
|
|
/* Pass over the current regex string in the line starting at ptr,
|
|
|
|
* null-terminate it, and return a pointer to the /next/ word. */
|
2002-09-06 20:35:28 +00:00
|
|
|
char *parse_next_regex(char *ptr)
|
|
|
|
{
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Continue until the end of line, or until a " followed by a
|
|
|
|
* blank character or the end of line. */
|
|
|
|
while (*ptr != '\0' && (*ptr != '"' ||
|
|
|
|
(*(ptr + 1) != '\0' && !isblank((unsigned char)ptr[1]))))
|
|
|
|
ptr++;
|
2002-09-06 20:35:28 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (*ptr == '\0') {
|
2018-01-19 18:50:30 +00:00
|
|
|
rcfile_error(N_("Regex strings must begin and end with a \" character"));
|
2017-12-29 18:27:33 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2002-09-06 20:35:28 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Null-terminate and advance ptr. */
|
|
|
|
*ptr++ = '\0';
|
2002-09-06 20:35:28 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
while (isblank((unsigned char)*ptr))
|
|
|
|
ptr++;
|
2002-09-06 20:35:28 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
return ptr;
|
2002-09-06 20:35:28 +00:00
|
|
|
}
|
|
|
|
|
2005-07-29 21:42:08 +00:00
|
|
|
/* Compile the regular expression regex to see if it's valid. Return
|
2016-03-11 16:39:27 +00:00
|
|
|
* TRUE if it is, and FALSE otherwise. */
|
2016-03-13 19:37:21 +00:00
|
|
|
bool nregcomp(const char *regex, int compile_flags)
|
2003-02-03 02:56:44 +00:00
|
|
|
{
|
2017-12-29 18:27:33 +00:00
|
|
|
regex_t preg;
|
2018-12-24 19:28:46 +00:00
|
|
|
int rc = regcomp(&preg, regex, compile_flags);
|
2003-02-03 02:56:44 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (rc != 0) {
|
|
|
|
size_t len = regerror(rc, &preg, NULL, 0);
|
|
|
|
char *str = charalloc(len);
|
2003-02-03 02:56:44 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
regerror(rc, &preg, str, len);
|
2018-12-24 19:28:46 +00:00
|
|
|
rcfile_error(N_("Bad regex \"%s\": %s"), regex, str);
|
2017-12-29 18:27:33 +00:00
|
|
|
free(str);
|
|
|
|
}
|
2005-03-04 15:09:55 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
regfree(&preg);
|
|
|
|
return (rc == 0);
|
2003-02-03 02:56:44 +00:00
|
|
|
}
|
|
|
|
|
2016-03-11 16:39:27 +00:00
|
|
|
/* Parse the next syntax name and its possible extension regexes from the
|
|
|
|
* line at ptr, and add it to the global linked list of color syntaxes. */
|
2002-08-21 16:10:37 +00:00
|
|
|
void parse_syntax(char *ptr)
|
2002-05-04 03:47:33 +00:00
|
|
|
{
|
2018-02-07 10:18:38 +00:00
|
|
|
char *nameptr = ptr;
|
2002-05-04 03:47:33 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
opensyntax = FALSE;
|
2016-02-28 20:38:14 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Check that the syntax name is not empty. */
|
|
|
|
if (*ptr == '\0' || (*ptr == '"' &&
|
|
|
|
(*(ptr + 1) == '\0' || *(ptr + 1) == '"'))) {
|
|
|
|
rcfile_error(N_("Missing syntax name"));
|
|
|
|
return;
|
|
|
|
}
|
2002-05-04 03:47:33 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
ptr = parse_next_word(ptr);
|
2016-03-09 20:28:50 +00:00
|
|
|
|
2018-02-07 10:25:46 +00:00
|
|
|
/* Check that quotes around the name are either paired or absent. */
|
2018-02-07 10:18:38 +00:00
|
|
|
if ((*nameptr == '\x22') ^ (nameptr[strlen(nameptr) - 1] == '\x22')) {
|
|
|
|
rcfile_error(N_("Unpaired quote in syntax name"));
|
2017-12-29 18:27:33 +00:00
|
|
|
return;
|
|
|
|
}
|
2005-03-10 20:55:11 +00:00
|
|
|
|
2018-02-07 10:18:38 +00:00
|
|
|
/* If the name is quoted, strip the quotes. */
|
|
|
|
if (*nameptr == '\x22') {
|
|
|
|
nameptr++;
|
|
|
|
nameptr[strlen(nameptr) - 1] = '\0';
|
|
|
|
}
|
2002-05-04 03:47:33 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Redefining the "none" syntax is not allowed. */
|
|
|
|
if (strcmp(nameptr, "none") == 0) {
|
|
|
|
rcfile_error(N_("The \"none\" syntax is reserved"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize a new syntax struct. */
|
|
|
|
live_syntax = (syntaxtype *)nmalloc(sizeof(syntaxtype));
|
|
|
|
live_syntax->name = mallocstrcpy(NULL, nameptr);
|
|
|
|
live_syntax->extensions = NULL;
|
|
|
|
live_syntax->headers = NULL;
|
|
|
|
live_syntax->magics = NULL;
|
|
|
|
live_syntax->linter = NULL;
|
2017-07-02 06:46:59 +00:00
|
|
|
#ifdef ENABLE_COMMENT
|
2017-12-29 18:27:33 +00:00
|
|
|
live_syntax->comment = mallocstrcpy(NULL, GENERAL_COMMENT_CHARACTER);
|
2017-07-02 06:46:59 +00:00
|
|
|
#endif
|
2017-12-29 18:27:33 +00:00
|
|
|
live_syntax->color = NULL;
|
|
|
|
lastcolor = NULL;
|
|
|
|
live_syntax->nmultis = 0;
|
2016-03-10 20:06:01 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Hook the new syntax in at the top of the list. */
|
|
|
|
live_syntax->next = syntaxes;
|
|
|
|
syntaxes = live_syntax;
|
2005-07-29 21:42:08 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
opensyntax = TRUE;
|
2016-02-28 20:38:14 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* The default syntax should have no associated extensions. */
|
|
|
|
if (strcmp(live_syntax->name, "default") == 0 && *ptr != '\0') {
|
2018-01-19 18:50:30 +00:00
|
|
|
rcfile_error(N_("The \"default\" syntax does not accept extensions"));
|
2017-12-29 18:27:33 +00:00
|
|
|
return;
|
|
|
|
}
|
2005-08-01 04:23:29 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* If there seem to be extension regexes, pick them up. */
|
|
|
|
if (*ptr != '\0')
|
|
|
|
grab_and_store("extension", ptr, &live_syntax->extensions);
|
2011-02-13 04:23:10 +00:00
|
|
|
}
|
2017-11-01 18:45:33 +00:00
|
|
|
#endif /* ENABLE_COLOR */
|
2002-05-04 03:47:33 +00:00
|
|
|
|
2018-03-01 10:16:59 +00:00
|
|
|
/* Return TRUE when the given function is present in almost all menus. */
|
2017-07-02 04:07:54 +00:00
|
|
|
bool is_universal(void (*func)(void))
|
2014-06-27 16:14:52 +00:00
|
|
|
{
|
2018-03-01 10:16:59 +00:00
|
|
|
return (func == do_left || func == do_right ||
|
2017-12-29 18:27:33 +00:00
|
|
|
func == do_home || func == do_end ||
|
2017-06-04 09:05:08 +00:00
|
|
|
#ifndef NANO_TINY
|
2017-12-29 18:27:33 +00:00
|
|
|
func == do_prev_word_void || func == do_next_word_void ||
|
2017-06-04 09:05:08 +00:00
|
|
|
#endif
|
2017-12-29 18:27:33 +00:00
|
|
|
func == do_delete || func == do_backspace ||
|
|
|
|
func == do_cut_text_void || func == do_uncut_text ||
|
2018-03-01 10:16:59 +00:00
|
|
|
func == do_tab || func == do_enter || func == do_verbatim_input);
|
2014-06-27 16:14:52 +00:00
|
|
|
}
|
|
|
|
|
2014-04-08 11:43:50 +00:00
|
|
|
/* Bind or unbind a key combo, to or from a function. */
|
2014-04-08 11:22:41 +00:00
|
|
|
void parse_binding(char *ptr, bool dobind)
|
2008-03-05 07:34:01 +00:00
|
|
|
{
|
2017-12-29 18:27:33 +00:00
|
|
|
char *keyptr = NULL, *keycopy = NULL, *funcptr = NULL, *menuptr = NULL;
|
2019-04-03 15:34:05 +00:00
|
|
|
keystruct *s, *newsc = NULL;
|
2018-10-24 16:12:06 +00:00
|
|
|
int menu, mask = 0;
|
2019-04-03 15:11:24 +00:00
|
|
|
funcstruct *f;
|
2008-03-05 07:34:01 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (*ptr == '\0') {
|
|
|
|
rcfile_error(N_("Missing key name"));
|
|
|
|
return;
|
2014-04-08 11:43:50 +00:00
|
|
|
}
|
2017-12-29 18:27:33 +00:00
|
|
|
|
|
|
|
keyptr = ptr;
|
2014-04-08 11:22:41 +00:00
|
|
|
ptr = parse_next_word(ptr);
|
2017-12-29 18:27:33 +00:00
|
|
|
keycopy = mallocstrcpy(NULL, keyptr);
|
2008-03-05 07:34:01 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (strlen(keycopy) < 2) {
|
|
|
|
rcfile_error(N_("Key name is too short"));
|
|
|
|
goto free_things;
|
2014-04-08 11:22:41 +00:00
|
|
|
}
|
2017-12-29 18:27:33 +00:00
|
|
|
|
|
|
|
/* Uppercase only the first two or three characters of the key name. */
|
|
|
|
keycopy[0] = toupper((unsigned char)keycopy[0]);
|
|
|
|
keycopy[1] = toupper((unsigned char)keycopy[1]);
|
|
|
|
if (keycopy[0] == 'M' && keycopy[1] == '-') {
|
|
|
|
if (strlen(keycopy) > 2)
|
|
|
|
keycopy[2] = toupper((unsigned char)keycopy[2]);
|
|
|
|
else {
|
|
|
|
rcfile_error(N_("Key name is too short"));
|
|
|
|
goto free_things;
|
|
|
|
}
|
2014-04-08 11:22:41 +00:00
|
|
|
}
|
2008-03-05 07:34:01 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* 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((unsigned char)keycopy[1]);
|
|
|
|
else if (keycopy[0] != '^' && keycopy[0] != 'M' && keycopy[0] != 'F') {
|
|
|
|
rcfile_error(N_("Key name must begin with \"^\", \"M\", or \"F\""));
|
|
|
|
goto free_things;
|
|
|
|
} else if (keycode_from_string(keycopy) < 0) {
|
|
|
|
rcfile_error(N_("Key name %s is invalid"), keycopy);
|
|
|
|
goto free_things;
|
|
|
|
}
|
2014-04-21 18:12:29 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (dobind) {
|
|
|
|
funcptr = ptr;
|
2018-01-20 12:01:43 +00:00
|
|
|
ptr = parse_argument(ptr);
|
2014-06-27 16:14:52 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (funcptr[0] == '\0') {
|
|
|
|
rcfile_error(N_("Must specify a function to bind the key to"));
|
|
|
|
goto free_things;
|
2018-05-24 18:07:39 +00:00
|
|
|
} else if (ptr == NULL)
|
|
|
|
goto free_things;
|
2017-12-29 18:27:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
menuptr = ptr;
|
|
|
|
ptr = parse_next_word(ptr);
|
|
|
|
|
|
|
|
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"));
|
|
|
|
goto free_things;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dobind) {
|
2018-01-20 12:01:43 +00:00
|
|
|
/* If the thing to bind starts with a double quote, it is a string,
|
|
|
|
* otherwise it is the name of a function. */
|
|
|
|
if (*funcptr == '"') {
|
2019-04-03 15:34:05 +00:00
|
|
|
newsc = nmalloc(sizeof(keystruct));
|
2018-03-18 18:17:56 +00:00
|
|
|
newsc->func = (functionptrtype)implant;
|
2018-01-20 12:01:43 +00:00
|
|
|
newsc->expansion = mallocstrcpy(NULL, funcptr + 1);
|
2018-02-14 16:47:08 +00:00
|
|
|
#ifndef NANO_TINY
|
2018-01-20 12:01:43 +00:00
|
|
|
newsc->toggle = 0;
|
2018-02-14 16:47:08 +00:00
|
|
|
#endif
|
2018-01-20 12:01:43 +00:00
|
|
|
} else
|
2018-08-28 10:31:53 +00:00
|
|
|
newsc = strtosc(funcptr);
|
2018-01-20 12:01:43 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (newsc == NULL) {
|
|
|
|
rcfile_error(N_("Cannot map name \"%s\" to a function"), funcptr);
|
|
|
|
goto free_things;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-11 07:51:33 +00:00
|
|
|
menu = name_to_menu(menuptr);
|
2017-12-29 18:27:33 +00:00
|
|
|
if (menu < 1) {
|
|
|
|
rcfile_error(N_("Cannot map name \"%s\" to a menu"), menuptr);
|
|
|
|
goto free_things;
|
|
|
|
}
|
|
|
|
|
2018-12-18 18:07:28 +00:00
|
|
|
/* Wipe the given shortcut from the given menu. */
|
|
|
|
for (s = sclist; s != NULL; s = s->next)
|
|
|
|
if ((s->menus & menu) && strcmp(s->keystr, keycopy) == 0)
|
|
|
|
s->menus &= ~menu;
|
2018-10-24 16:05:35 +00:00
|
|
|
|
2018-12-18 18:18:10 +00:00
|
|
|
/* When unbinding, we are done now. */
|
|
|
|
if (!dobind)
|
|
|
|
goto free_things;
|
2018-10-24 16:05:35 +00:00
|
|
|
|
2018-10-24 16:12:06 +00:00
|
|
|
/* Tally up the menus where the function exists. */
|
|
|
|
for (f = allfuncs; f != NULL; f = f->next)
|
|
|
|
if (f->func == newsc->func)
|
|
|
|
mask = mask | f->menus;
|
2014-06-27 16:14:52 +00:00
|
|
|
|
2016-09-01 07:36:47 +00:00
|
|
|
#ifndef NANO_TINY
|
2018-10-24 16:12:06 +00:00
|
|
|
/* Handle the special case of the toggles. */
|
|
|
|
if (newsc->func == do_toggle_void)
|
|
|
|
mask = MMAIN;
|
2016-09-01 07:36:47 +00:00
|
|
|
#endif
|
2018-03-01 10:08:13 +00:00
|
|
|
#ifdef ENABLE_NANORC
|
2018-10-24 16:12:06 +00:00
|
|
|
/* Handle the special case of a key defined as a string. */
|
|
|
|
if (newsc->func == (functionptrtype)implant)
|
|
|
|
mask = MMOST | MHELP;
|
2018-03-01 10:08:13 +00:00
|
|
|
#endif
|
2018-10-24 16:12:06 +00:00
|
|
|
/* Now limit the given menu to those where the function exists. */
|
|
|
|
menu = menu & (is_universal(newsc->func) ? MMOST : mask);
|
2014-06-27 16:14:52 +00:00
|
|
|
|
2018-10-24 16:12:06 +00:00
|
|
|
if (!menu) {
|
2019-03-31 18:05:30 +00:00
|
|
|
if (!ISSET(RESTRICTED) && !ISSET(VIEW_MODE))
|
2018-10-24 16:12:06 +00:00
|
|
|
rcfile_error(N_("Function '%s' does not exist in menu '%s'"),
|
|
|
|
funcptr, menuptr);
|
|
|
|
goto free_things;
|
|
|
|
}
|
2014-06-27 16:14:52 +00:00
|
|
|
|
2018-10-24 16:12:06 +00:00
|
|
|
newsc->menus = menu;
|
|
|
|
assign_keyinfo(newsc, keycopy, 0);
|
2008-03-05 07:34:01 +00:00
|
|
|
|
2018-12-30 18:28:17 +00:00
|
|
|
/* Disallow rebinding ^[ and frequent escape-sequence starter "Esc [". */
|
|
|
|
if ((!newsc->meta && newsc->keycode == ESC_CODE) ||
|
|
|
|
(newsc->meta && newsc->keycode == '[')) {
|
|
|
|
rcfile_error(N_("Keystroke %s may not be rebound"), keycopy);
|
2018-12-18 18:18:10 +00:00
|
|
|
free_things:
|
|
|
|
free(keycopy);
|
|
|
|
free(newsc);
|
|
|
|
return;
|
2018-10-24 16:12:06 +00:00
|
|
|
}
|
2009-11-21 16:26:59 +00:00
|
|
|
|
2016-09-01 07:36:47 +00:00
|
|
|
#ifndef NANO_TINY
|
2018-10-24 16:12:06 +00:00
|
|
|
/* If this is a toggle, find and copy its sequence number. */
|
|
|
|
if (newsc->func == do_toggle_void) {
|
|
|
|
for (s = sclist; s != NULL; s = s->next)
|
|
|
|
if (s->func == do_toggle_void && s->toggle == newsc->toggle)
|
|
|
|
newsc->ordinal = s->ordinal;
|
|
|
|
} else
|
|
|
|
newsc->ordinal = 0;
|
2016-09-01 07:36:47 +00:00
|
|
|
#endif
|
2018-10-24 16:12:06 +00:00
|
|
|
/* Add the new shortcut at the start of the list. */
|
|
|
|
newsc->next = sclist;
|
|
|
|
sclist = newsc;
|
2009-11-21 16:26:59 +00:00
|
|
|
}
|
|
|
|
|
2017-10-27 21:15:06 +00:00
|
|
|
/* Verify that the given file exists, is not a folder nor a device. */
|
2016-11-27 16:27:04 +00:00
|
|
|
bool is_good_file(char *file)
|
|
|
|
{
|
2017-12-29 18:27:33 +00:00
|
|
|
struct stat rcinfo;
|
|
|
|
|
|
|
|
/* First check that the file exists and is readable. */
|
|
|
|
if (access(file, R_OK) != 0)
|
|
|
|
return FALSE;
|
|
|
|
|
2018-05-27 16:35:52 +00:00
|
|
|
/* If the thing exists, it may be neither a directory nor a device. */
|
2017-12-29 18:27:33 +00:00
|
|
|
if (stat(file, &rcinfo) != -1 && (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"), file);
|
|
|
|
return FALSE;
|
|
|
|
} else
|
|
|
|
return TRUE;
|
2016-11-27 16:27:04 +00:00
|
|
|
}
|
|
|
|
|
2017-11-01 18:45:33 +00:00
|
|
|
#ifdef ENABLE_COLOR
|
2016-03-12 09:43:10 +00:00
|
|
|
/* Read and parse one included syntax file. */
|
|
|
|
static void parse_one_include(char *file)
|
2006-04-13 02:43:54 +00:00
|
|
|
{
|
2017-12-29 18:27:33 +00:00
|
|
|
FILE *rcstream;
|
2006-04-13 02:43:54 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Don't open directories, character files, or block files. */
|
|
|
|
if (!is_good_file(file))
|
|
|
|
return;
|
2006-04-13 02:43:54 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Open the included syntax file. */
|
|
|
|
rcstream = fopen(file, "rb");
|
2016-11-26 16:41:31 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (rcstream == NULL) {
|
|
|
|
rcfile_error(_("Error reading %s: %s"), file, strerror(errno));
|
|
|
|
return;
|
|
|
|
}
|
2006-04-13 02:43:54 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Use the name and line number position of the included syntax file
|
|
|
|
* while parsing it, so we can know where any errors in it are. */
|
|
|
|
nanorc = file;
|
|
|
|
lineno = 0;
|
2006-04-13 02:43:54 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
parse_rcfile(rcstream, TRUE);
|
2014-03-30 20:37:40 +00:00
|
|
|
}
|
|
|
|
|
2016-03-12 09:43:10 +00:00
|
|
|
/* Expand globs in the passed name, and parse the resultant files. */
|
|
|
|
void parse_includes(char *ptr)
|
2014-03-30 20:37:40 +00:00
|
|
|
{
|
2018-11-19 10:47:02 +00:00
|
|
|
char *pattern, *expanded;
|
|
|
|
char *was_nanorc = nanorc;
|
|
|
|
size_t was_lineno = lineno;
|
2017-12-29 18:27:33 +00:00
|
|
|
glob_t files;
|
2018-11-19 11:00:18 +00:00
|
|
|
int result;
|
2017-12-29 18:27:33 +00:00
|
|
|
|
2018-11-19 10:47:02 +00:00
|
|
|
pattern = ptr;
|
|
|
|
if (*pattern == '"')
|
|
|
|
pattern++;
|
2017-12-29 18:27:33 +00:00
|
|
|
ptr = parse_argument(ptr);
|
|
|
|
|
2018-11-19 11:00:18 +00:00
|
|
|
/* Expand a tilde first, then try to match the globbing pattern. */
|
2018-11-19 10:47:02 +00:00
|
|
|
expanded = real_dir_from_tilde(pattern);
|
2019-04-02 18:40:48 +00:00
|
|
|
result = glob(expanded, GLOB_ERR, NULL, &files);
|
2017-12-29 18:27:33 +00:00
|
|
|
|
2018-11-19 11:00:18 +00:00
|
|
|
/* If there are matches, process each of them. Otherwise, only
|
|
|
|
* report an error if it's something other than zero matches. */
|
|
|
|
if (result == 0) {
|
2018-11-19 10:47:02 +00:00
|
|
|
for (size_t i = 0; i < files.gl_pathc; ++i)
|
2017-12-29 18:27:33 +00:00
|
|
|
parse_one_include(files.gl_pathv[i]);
|
2018-11-19 11:00:18 +00:00
|
|
|
} else if (result != GLOB_NOMATCH)
|
2018-11-19 10:47:02 +00:00
|
|
|
rcfile_error(_("Error expanding %s: %s"), pattern, strerror(errno));
|
2017-12-29 18:27:33 +00:00
|
|
|
|
|
|
|
globfree(&files);
|
|
|
|
free(expanded);
|
|
|
|
|
|
|
|
/* We're done with the included file(s). Restore the original
|
|
|
|
* filename and line number position. */
|
2018-11-19 10:47:02 +00:00
|
|
|
nanorc = was_nanorc;
|
|
|
|
lineno = was_lineno;
|
2006-04-13 02:43:54 +00:00
|
|
|
}
|
|
|
|
|
2006-05-28 19:00:16 +00:00
|
|
|
/* Return the short value corresponding to the color named in colorname,
|
|
|
|
* and set bright to TRUE if that color is bright. */
|
2013-03-17 22:09:38 +00:00
|
|
|
short color_to_short(const char *colorname, bool *bright)
|
2006-05-28 19:00:16 +00:00
|
|
|
{
|
2017-12-29 18:27:33 +00:00
|
|
|
if (strncasecmp(colorname, "bright", 6) == 0) {
|
|
|
|
*bright = TRUE;
|
|
|
|
colorname += 6;
|
2018-01-15 12:15:23 +00:00
|
|
|
} else
|
|
|
|
*bright = FALSE;
|
2017-12-29 18:27:33 +00:00
|
|
|
|
|
|
|
if (strcasecmp(colorname, "green") == 0)
|
|
|
|
return COLOR_GREEN;
|
|
|
|
else if (strcasecmp(colorname, "red") == 0)
|
|
|
|
return COLOR_RED;
|
|
|
|
else if (strcasecmp(colorname, "blue") == 0)
|
|
|
|
return COLOR_BLUE;
|
|
|
|
else if (strcasecmp(colorname, "white") == 0)
|
|
|
|
return COLOR_WHITE;
|
|
|
|
else if (strcasecmp(colorname, "yellow") == 0)
|
|
|
|
return COLOR_YELLOW;
|
|
|
|
else if (strcasecmp(colorname, "cyan") == 0)
|
|
|
|
return COLOR_CYAN;
|
|
|
|
else if (strcasecmp(colorname, "magenta") == 0)
|
|
|
|
return COLOR_MAGENTA;
|
|
|
|
else if (strcasecmp(colorname, "black") == 0)
|
|
|
|
return COLOR_BLACK;
|
2018-03-27 11:31:37 +00:00
|
|
|
else if (strcasecmp(colorname, "normal") == 0)
|
2018-04-01 07:49:58 +00:00
|
|
|
return USE_THE_DEFAULT;
|
2017-12-29 18:27:33 +00:00
|
|
|
|
2018-01-15 12:21:39 +00:00
|
|
|
rcfile_error(N_("Color \"%s\" not understood"), colorname);
|
2018-04-01 07:49:58 +00:00
|
|
|
return BAD_COLOR;
|
2006-05-28 19:00:16 +00:00
|
|
|
}
|
|
|
|
|
2018-03-30 08:25:39 +00:00
|
|
|
/* Parse the color name (or pair of color names) in the given string.
|
|
|
|
* Return FALSE when any color name is invalid; otherwise return TRUE. */
|
2018-08-24 00:50:16 +00:00
|
|
|
bool parse_color_names(char *combostr, short *fg, short *bg, int *attributes)
|
2018-03-30 08:25:39 +00:00
|
|
|
{
|
|
|
|
char *comma = strchr(combostr, ',');
|
2018-08-24 00:50:16 +00:00
|
|
|
bool bright;
|
|
|
|
|
|
|
|
*attributes = A_NORMAL;
|
2018-03-30 08:25:39 +00:00
|
|
|
|
|
|
|
if (comma != NULL) {
|
2018-08-24 00:50:16 +00:00
|
|
|
*bg = color_to_short(comma + 1, &bright);
|
|
|
|
if (bright) {
|
2018-03-30 08:25:39 +00:00
|
|
|
rcfile_error(N_("A background color cannot be bright"));
|
|
|
|
return FALSE;
|
|
|
|
}
|
2018-04-01 07:49:58 +00:00
|
|
|
if (*bg == BAD_COLOR)
|
2018-03-30 08:25:39 +00:00
|
|
|
return FALSE;
|
|
|
|
*comma = '\0';
|
|
|
|
} else
|
2018-04-01 07:49:58 +00:00
|
|
|
*bg = USE_THE_DEFAULT;
|
2018-03-30 08:25:39 +00:00
|
|
|
|
|
|
|
if (comma != combostr) {
|
2018-08-24 00:50:16 +00:00
|
|
|
*fg = color_to_short(combostr, &bright);
|
2018-04-01 07:49:58 +00:00
|
|
|
if (*fg == BAD_COLOR)
|
2018-03-30 08:25:39 +00:00
|
|
|
return FALSE;
|
2018-08-24 00:50:16 +00:00
|
|
|
|
|
|
|
if (bright)
|
|
|
|
*attributes = A_BOLD;
|
2018-03-30 08:25:39 +00:00
|
|
|
} else
|
2018-04-01 07:49:58 +00:00
|
|
|
*fg = USE_THE_DEFAULT;
|
2018-03-30 08:25:39 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2005-12-08 07:09:08 +00:00
|
|
|
/* Parse the color string in the line at ptr, and add it to the current
|
2016-03-13 19:37:21 +00:00
|
|
|
* file's associated colors. rex_flags are the regex compilation flags
|
|
|
|
* to use, excluding or including REG_ICASE for case (in)sensitivity. */
|
|
|
|
void parse_colors(char *ptr, int rex_flags)
|
2001-11-29 02:42:27 +00:00
|
|
|
{
|
2017-12-29 18:27:33 +00:00
|
|
|
short fg, bg;
|
2018-08-24 00:50:16 +00:00
|
|
|
int attributes;
|
2017-12-29 18:27:33 +00:00
|
|
|
char *item;
|
|
|
|
|
|
|
|
if (!opensyntax) {
|
2018-01-19 18:50:30 +00:00
|
|
|
rcfile_error(N_("A '%s' command requires a preceding 'syntax' command"),
|
|
|
|
"color");
|
2017-12-29 18:27:33 +00:00
|
|
|
return;
|
|
|
|
}
|
2006-04-13 02:43:54 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (*ptr == '\0') {
|
|
|
|
rcfile_error(N_("Missing color name"));
|
|
|
|
return;
|
|
|
|
}
|
2001-11-29 02:42:27 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
item = ptr;
|
|
|
|
ptr = parse_next_word(ptr);
|
2018-08-24 00:50:16 +00:00
|
|
|
if (!parse_color_names(item, &fg, &bg, &attributes))
|
2017-12-29 18:27:33 +00:00
|
|
|
return;
|
2003-02-07 00:19:05 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (*ptr == '\0') {
|
|
|
|
rcfile_error(N_("Missing regex string after '%s' command"), "color");
|
|
|
|
return;
|
2002-01-19 16:52:34 +00:00
|
|
|
}
|
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Now for the fun part. Start adding regexes to individual strings
|
|
|
|
* in the colorstrings array, woo! */
|
|
|
|
while (ptr != NULL && *ptr != '\0') {
|
|
|
|
colortype *newcolor = NULL;
|
|
|
|
/* The container for a color plus its regexes. */
|
|
|
|
bool goodstart;
|
|
|
|
/* Whether the start expression was valid. */
|
|
|
|
bool expectend = FALSE;
|
|
|
|
/* Whether to expect an end= line. */
|
|
|
|
|
|
|
|
if (strncasecmp(ptr, "start=", 6) == 0) {
|
|
|
|
ptr += 6;
|
|
|
|
expectend = TRUE;
|
|
|
|
}
|
2005-03-10 20:55:11 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (*ptr != '"') {
|
2018-01-19 18:50:30 +00:00
|
|
|
rcfile_error(N_("Regex strings must begin and end with a \" character"));
|
2017-12-29 18:27:33 +00:00
|
|
|
ptr = parse_next_regex(ptr);
|
|
|
|
continue;
|
|
|
|
}
|
2005-03-10 20:55:11 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
item = ++ptr;
|
|
|
|
ptr = parse_next_regex(ptr);
|
|
|
|
if (ptr == NULL)
|
|
|
|
break;
|
2016-03-12 10:21:02 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (*item == '\0') {
|
|
|
|
rcfile_error(N_("Empty regex string"));
|
|
|
|
goodstart = FALSE;
|
|
|
|
} else
|
|
|
|
goodstart = nregcomp(item, rex_flags);
|
2016-03-04 20:50:38 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* If the starting regex is valid, initialize a new color struct,
|
|
|
|
* and hook it in at the tail of the linked list. */
|
|
|
|
if (goodstart) {
|
|
|
|
newcolor = (colortype *)nmalloc(sizeof(colortype));
|
2005-07-29 21:42:08 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
newcolor->fg = fg;
|
|
|
|
newcolor->bg = bg;
|
2018-08-24 00:50:16 +00:00
|
|
|
newcolor->attributes = attributes;
|
2017-12-29 18:27:33 +00:00
|
|
|
newcolor->rex_flags = rex_flags;
|
2005-07-29 21:42:08 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
newcolor->start_regex = mallocstrcpy(NULL, item);
|
|
|
|
newcolor->start = NULL;
|
2005-07-29 21:42:08 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
newcolor->end_regex = NULL;
|
|
|
|
newcolor->end = NULL;
|
2003-02-03 02:56:44 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
newcolor->next = NULL;
|
2005-07-29 21:42:08 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (lastcolor == NULL)
|
|
|
|
live_syntax->color = newcolor;
|
|
|
|
else
|
|
|
|
lastcolor->next = newcolor;
|
2002-07-19 01:08:59 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
lastcolor = newcolor;
|
|
|
|
}
|
2005-03-10 20:55:11 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (!expectend)
|
|
|
|
continue;
|
2003-02-03 02:56:44 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (ptr == NULL || strncasecmp(ptr, "end=", 4) != 0) {
|
|
|
|
rcfile_error(N_("\"start=\" requires a corresponding \"end=\""));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr += 4;
|
|
|
|
if (*ptr != '"') {
|
|
|
|
rcfile_error(N_("Regex strings must begin and end with a \" character"));
|
|
|
|
continue;
|
|
|
|
}
|
2005-03-10 20:55:11 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
item = ++ptr;
|
|
|
|
ptr = parse_next_regex(ptr);
|
|
|
|
if (ptr == NULL)
|
|
|
|
break;
|
2009-01-25 07:25:17 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (*item == '\0') {
|
|
|
|
rcfile_error(N_("Empty regex string"));
|
|
|
|
continue;
|
|
|
|
}
|
2017-01-16 15:28:48 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* If the start regex was invalid, skip past the end regex
|
|
|
|
* to stay in sync. */
|
|
|
|
if (!goodstart)
|
|
|
|
continue;
|
2016-03-12 09:52:16 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* If it's valid, save the ending regex string. */
|
|
|
|
if (nregcomp(item, rex_flags))
|
|
|
|
newcolor->end_regex = mallocstrcpy(NULL, item);
|
2016-03-12 09:52:16 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Lame way to skip another static counter. */
|
|
|
|
newcolor->id = live_syntax->nmultis;
|
|
|
|
live_syntax->nmultis++;
|
|
|
|
}
|
2001-11-29 02:42:27 +00:00
|
|
|
}
|
2008-09-21 23:02:30 +00:00
|
|
|
|
2018-01-15 12:06:35 +00:00
|
|
|
/* Parse the argument of an interface color option. */
|
2018-01-12 10:47:07 +00:00
|
|
|
colortype *parse_interface_color(char *combostr)
|
|
|
|
{
|
2018-01-15 12:06:35 +00:00
|
|
|
colortype *trio = nmalloc(sizeof(colortype));
|
2018-01-12 10:47:07 +00:00
|
|
|
|
2018-08-24 00:50:16 +00:00
|
|
|
if (parse_color_names(combostr, &trio->fg, &trio->bg, &trio->attributes)) {
|
2018-01-15 12:06:35 +00:00
|
|
|
free(combostr);
|
|
|
|
return trio;
|
|
|
|
} else {
|
|
|
|
free(combostr);
|
|
|
|
free(trio);
|
|
|
|
return NULL;
|
|
|
|
}
|
2018-01-12 10:47:07 +00:00
|
|
|
}
|
|
|
|
|
2016-02-28 15:16:27 +00:00
|
|
|
/* Read regex strings enclosed in double quotes from the line pointed at
|
|
|
|
* by ptr, and store them quoteless in the passed storage place. */
|
2016-03-10 11:00:59 +00:00
|
|
|
void grab_and_store(const char *kind, char *ptr, regexlisttype **storage)
|
2016-02-28 15:16:27 +00:00
|
|
|
{
|
2017-12-29 18:27:33 +00:00
|
|
|
regexlisttype *lastthing;
|
2008-09-21 23:02:30 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (!opensyntax) {
|
|
|
|
rcfile_error(
|
|
|
|
N_("A '%s' command requires a preceding 'syntax' command"), kind);
|
|
|
|
return;
|
|
|
|
}
|
2016-03-10 09:46:21 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* The default syntax doesn't take any file matching stuff. */
|
|
|
|
if (strcmp(live_syntax->name, "default") == 0 && *ptr != '\0') {
|
|
|
|
rcfile_error(
|
|
|
|
N_("The \"default\" syntax does not accept '%s' regexes"), kind);
|
|
|
|
return;
|
|
|
|
}
|
2008-09-21 23:02:30 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (*ptr == '\0') {
|
|
|
|
rcfile_error(N_("Missing regex string after '%s' command"), kind);
|
|
|
|
return;
|
|
|
|
}
|
2008-09-21 23:02:30 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
lastthing = *storage;
|
2016-02-28 16:36:23 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* If there was an earlier command, go to the last of those regexes. */
|
|
|
|
while (lastthing != NULL && lastthing->next != NULL)
|
|
|
|
lastthing = lastthing->next;
|
2016-02-28 16:36:23 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Now gather any valid regexes and add them to the linked list. */
|
|
|
|
while (*ptr != '\0') {
|
|
|
|
const char *regexstring;
|
|
|
|
regexlisttype *newthing;
|
2008-09-21 23:02:30 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (*ptr != '"') {
|
2018-01-19 18:50:30 +00:00
|
|
|
rcfile_error(N_("Regex strings must begin and end with a \" character"));
|
2017-12-29 18:27:33 +00:00
|
|
|
return;
|
|
|
|
}
|
2008-09-21 23:02:30 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
regexstring = ++ptr;
|
|
|
|
ptr = parse_next_regex(ptr);
|
|
|
|
if (ptr == NULL)
|
|
|
|
return;
|
2008-09-21 23:02:30 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* If the regex string is malformed, skip it. */
|
|
|
|
if (!nregcomp(regexstring, NANO_REG_EXTENDED | REG_NOSUB))
|
|
|
|
continue;
|
2008-09-21 23:02:30 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Copy the regex into a struct, and hook this in at the end. */
|
|
|
|
newthing = (regexlisttype *)nmalloc(sizeof(regexlisttype));
|
|
|
|
newthing->full_regex = mallocstrcpy(NULL, regexstring);
|
|
|
|
newthing->next = NULL;
|
2008-09-21 23:02:30 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (lastthing == NULL)
|
|
|
|
*storage = newthing;
|
|
|
|
else
|
|
|
|
lastthing->next = newthing;
|
2016-02-28 15:47:37 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
lastthing = newthing;
|
|
|
|
}
|
2008-09-21 23:02:30 +00:00
|
|
|
}
|
2014-02-24 10:18:15 +00:00
|
|
|
|
2018-07-10 14:24:22 +00:00
|
|
|
/* Gather and store the string after a comment/linter command. */
|
2016-03-10 10:36:49 +00:00
|
|
|
void pick_up_name(const char *kind, char *ptr, char **storage)
|
2014-02-24 10:18:15 +00:00
|
|
|
{
|
2017-12-29 18:27:33 +00:00
|
|
|
if (!opensyntax) {
|
|
|
|
rcfile_error(
|
|
|
|
N_("A '%s' command requires a preceding 'syntax' command"), kind);
|
|
|
|
return;
|
|
|
|
}
|
2015-01-03 07:24:17 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (*ptr == '\0') {
|
|
|
|
rcfile_error(N_("Missing argument after '%s'"), kind);
|
|
|
|
return;
|
|
|
|
}
|
2015-01-03 07:24:17 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* If the argument starts with a quote, find the terminating quote. */
|
|
|
|
if (*ptr == '"') {
|
2018-01-19 19:27:53 +00:00
|
|
|
char *look = ptr + strlen(ptr);
|
2017-07-01 11:25:11 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
while (*look != '"') {
|
2018-01-19 19:27:53 +00:00
|
|
|
if (--look == ptr) {
|
2017-12-29 18:27:33 +00:00
|
|
|
rcfile_error(N_("Argument of '%s' lacks closing \""), kind);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2018-01-19 19:27:53 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
*look = '\0';
|
2018-01-19 19:27:53 +00:00
|
|
|
ptr++;
|
2016-05-25 20:13:50 +00:00
|
|
|
}
|
2017-07-06 11:03:20 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
*storage = mallocstrcpy(*storage, ptr);
|
2015-01-03 07:24:17 +00:00
|
|
|
}
|
2017-11-01 18:45:33 +00:00
|
|
|
#endif /* ENABLE_COLOR */
|
2014-02-24 10:18:15 +00:00
|
|
|
|
2016-11-27 15:34:34 +00:00
|
|
|
/* Verify that the user has not unmapped every shortcut for a
|
|
|
|
* function that we consider 'vital' (such as "Exit"). */
|
2008-03-20 04:45:55 +00:00
|
|
|
static void check_vitals_mapped(void)
|
|
|
|
{
|
2019-04-03 15:11:24 +00:00
|
|
|
funcstruct *f;
|
2017-12-29 18:27:33 +00:00
|
|
|
int v;
|
2018-08-05 18:07:32 +00:00
|
|
|
#define VITALS 3
|
|
|
|
void (*vitals[VITALS])(void) = { do_exit, do_exit, do_cancel };
|
|
|
|
int inmenus[VITALS] = { MMAIN, MHELP, MYESNO };
|
2017-12-29 18:27:33 +00:00
|
|
|
|
|
|
|
for (v = 0; v < VITALS; v++) {
|
|
|
|
for (f = allfuncs; f != NULL; f = f->next) {
|
2018-02-24 18:31:11 +00:00
|
|
|
if (f->func == vitals[v] && f->menus & inmenus[v]) {
|
2019-04-03 15:34:05 +00:00
|
|
|
const keystruct *s = first_sc_for(inmenus[v], f->func);
|
2017-12-29 18:27:33 +00:00
|
|
|
if (!s) {
|
2018-08-06 17:31:46 +00:00
|
|
|
fprintf(stderr, _("No key is bound to function '%s' in "
|
2018-08-06 19:29:01 +00:00
|
|
|
"menu '%s'. Exiting.\n"), f->desc,
|
|
|
|
menu_to_name(inmenus[v]));
|
2017-12-29 18:27:33 +00:00
|
|
|
fprintf(stderr, _("If needed, use nano with the -I option "
|
|
|
|
"to adjust your nanorc settings.\n"));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2014-06-20 10:48:26 +00:00
|
|
|
}
|
|
|
|
}
|
2008-03-20 04:45:55 +00:00
|
|
|
}
|
|
|
|
|
2006-06-01 20:23:24 +00:00
|
|
|
/* Parse the rcfile, once it has been opened successfully at rcstream,
|
2016-11-27 15:34:34 +00:00
|
|
|
* and close it afterwards. If syntax_only is TRUE, allow the file to
|
|
|
|
* to contain only color syntax commands. */
|
|
|
|
void parse_rcfile(FILE *rcstream, bool syntax_only)
|
2001-04-18 04:28:54 +00:00
|
|
|
{
|
2017-12-29 18:27:33 +00:00
|
|
|
char *buf = NULL;
|
|
|
|
ssize_t len;
|
|
|
|
size_t n = 0;
|
2005-03-10 20:55:11 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
while ((len = getline(&buf, &n, rcstream)) > 0) {
|
|
|
|
char *ptr, *keyword, *option;
|
|
|
|
int set = 0;
|
|
|
|
size_t i;
|
2005-03-10 20:55:11 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Ignore the newline. */
|
|
|
|
if (buf[len - 1] == '\n')
|
|
|
|
buf[len - 1] = '\0';
|
2001-04-18 04:28:54 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
lineno++;
|
|
|
|
ptr = buf;
|
|
|
|
while (isblank((unsigned char)*ptr))
|
|
|
|
ptr++;
|
2001-04-18 04:28:54 +00:00
|
|
|
|
2018-11-05 08:38:07 +00:00
|
|
|
/* If the line is empty or a comment, skip to next line. */
|
2017-12-29 18:27:33 +00:00
|
|
|
if (*ptr == '\0' || *ptr == '#')
|
|
|
|
continue;
|
2001-04-18 04:28:54 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Otherwise, skip to the next space. */
|
|
|
|
keyword = ptr;
|
|
|
|
ptr = parse_next_word(ptr);
|
2001-04-18 04:28:54 +00:00
|
|
|
|
2017-11-01 18:45:33 +00:00
|
|
|
#ifdef ENABLE_COLOR
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Handle extending first... */
|
2018-08-04 05:29:36 +00:00
|
|
|
if (strcasecmp(keyword, "extendsyntax") == 0 && !syntax_only) {
|
2017-12-29 18:27:33 +00:00
|
|
|
syntaxtype *sint;
|
|
|
|
char *syntaxname = ptr;
|
|
|
|
|
|
|
|
ptr = parse_next_word(ptr);
|
|
|
|
|
|
|
|
for (sint = syntaxes; sint != NULL; sint = sint->next)
|
|
|
|
if (!strcmp(sint->name, syntaxname))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (sint == NULL) {
|
|
|
|
rcfile_error(N_("Could not find syntax \"%s\" to extend"),
|
|
|
|
syntaxname);
|
|
|
|
opensyntax = FALSE;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
live_syntax = sint;
|
|
|
|
opensyntax = TRUE;
|
|
|
|
|
|
|
|
/* Refind the tail of the color list for this syntax. */
|
|
|
|
lastcolor = sint->color;
|
|
|
|
if (lastcolor != NULL)
|
|
|
|
while (lastcolor->next != NULL)
|
|
|
|
lastcolor = lastcolor->next;
|
|
|
|
|
|
|
|
keyword = ptr;
|
|
|
|
ptr = parse_next_word(ptr);
|
|
|
|
}
|
2014-03-02 05:27:56 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Try to parse the keyword. */
|
|
|
|
if (strcasecmp(keyword, "syntax") == 0) {
|
|
|
|
if (opensyntax && lastcolor == NULL)
|
|
|
|
rcfile_error(N_("Syntax \"%s\" has no color commands"),
|
|
|
|
live_syntax->name);
|
|
|
|
parse_syntax(ptr);
|
|
|
|
}
|
|
|
|
else if (strcasecmp(keyword, "header") == 0)
|
|
|
|
grab_and_store("header", ptr, &live_syntax->headers);
|
|
|
|
else if (strcasecmp(keyword, "magic") == 0)
|
2015-04-03 17:28:30 +00:00
|
|
|
#ifdef HAVE_LIBMAGIC
|
2017-12-29 18:27:33 +00:00
|
|
|
grab_and_store("magic", ptr, &live_syntax->magics);
|
2015-04-03 17:28:30 +00:00
|
|
|
#else
|
2017-12-29 18:27:33 +00:00
|
|
|
;
|
2016-05-25 20:13:50 +00:00
|
|
|
#endif
|
2017-12-29 18:27:33 +00:00
|
|
|
else if (strcasecmp(keyword, "comment") == 0)
|
2016-05-25 20:13:50 +00:00
|
|
|
#ifdef ENABLE_COMMENT
|
2017-12-29 18:27:33 +00:00
|
|
|
pick_up_name("comment", ptr, &live_syntax->comment);
|
2016-05-25 20:13:50 +00:00
|
|
|
#else
|
2017-12-29 18:27:33 +00:00
|
|
|
;
|
2015-04-03 17:28:30 +00:00
|
|
|
#endif
|
2017-12-29 18:27:33 +00:00
|
|
|
else if (strcasecmp(keyword, "color") == 0)
|
|
|
|
parse_colors(ptr, NANO_REG_EXTENDED);
|
|
|
|
else if (strcasecmp(keyword, "icolor") == 0)
|
|
|
|
parse_colors(ptr, NANO_REG_EXTENDED | REG_ICASE);
|
|
|
|
else if (strcasecmp(keyword, "linter") == 0)
|
|
|
|
pick_up_name("linter", ptr, &live_syntax->linter);
|
2018-08-04 05:29:36 +00:00
|
|
|
else if (syntax_only && (strcasecmp(keyword, "set") == 0 ||
|
|
|
|
strcasecmp(keyword, "unset") == 0 ||
|
|
|
|
strcasecmp(keyword, "bind") == 0 ||
|
|
|
|
strcasecmp(keyword, "unbind") == 0 ||
|
|
|
|
strcasecmp(keyword, "include") == 0 ||
|
|
|
|
strcasecmp(keyword, "extendsyntax") == 0))
|
2017-12-29 18:27:33 +00:00
|
|
|
rcfile_error(N_("Command \"%s\" not allowed in included file"),
|
|
|
|
keyword);
|
|
|
|
else if (strcasecmp(keyword, "include") == 0)
|
|
|
|
parse_includes(ptr);
|
|
|
|
else
|
2017-11-01 18:45:33 +00:00
|
|
|
#endif /* ENABLE_COLOR */
|
2017-12-29 18:27:33 +00:00
|
|
|
if (strcasecmp(keyword, "set") == 0)
|
|
|
|
set = 1;
|
|
|
|
else if (strcasecmp(keyword, "unset") == 0)
|
|
|
|
set = -1;
|
|
|
|
else if (strcasecmp(keyword, "bind") == 0)
|
|
|
|
parse_binding(ptr, TRUE);
|
|
|
|
else if (strcasecmp(keyword, "unbind") == 0)
|
|
|
|
parse_binding(ptr, FALSE);
|
|
|
|
else
|
|
|
|
rcfile_error(N_("Command \"%s\" not understood"), keyword);
|
2005-03-10 20:55:11 +00:00
|
|
|
|
2017-11-01 18:45:33 +00:00
|
|
|
#ifdef ENABLE_COLOR
|
2017-12-29 18:27:33 +00:00
|
|
|
/* If a syntax was extended, it stops at the end of the command. */
|
|
|
|
if (live_syntax != syntaxes)
|
|
|
|
opensyntax = FALSE;
|
2014-03-02 05:27:56 +00:00
|
|
|
#endif
|
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (set == 0)
|
|
|
|
continue;
|
2005-03-10 20:55:11 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (*ptr == '\0') {
|
|
|
|
rcfile_error(N_("Missing option"));
|
|
|
|
continue;
|
|
|
|
}
|
2001-04-18 04:28:54 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
option = ptr;
|
|
|
|
ptr = parse_next_word(ptr);
|
2001-04-18 04:28:54 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Find the just read name among the existing options. */
|
|
|
|
for (i = 0; rcopts[i].name != NULL; i++) {
|
|
|
|
if (strcasecmp(option, rcopts[i].name) == 0)
|
|
|
|
break;
|
|
|
|
}
|
2016-03-14 17:14:35 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
if (rcopts[i].name == NULL) {
|
|
|
|
rcfile_error(N_("Unknown option \"%s\""), option);
|
|
|
|
continue;
|
|
|
|
}
|
2016-03-14 17:14:35 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* First handle unsetting. */
|
|
|
|
if (set == -1) {
|
|
|
|
if (rcopts[i].flag != 0)
|
|
|
|
UNSET(rcopts[i].flag);
|
|
|
|
else
|
|
|
|
rcfile_error(N_("Cannot unset option \"%s\""), rcopts[i].name);
|
|
|
|
continue;
|
|
|
|
}
|
2016-03-14 17:14:35 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* If the option has a flag, it doesn't take an argument. */
|
|
|
|
if (rcopts[i].flag != 0) {
|
|
|
|
SET(rcopts[i].flag);
|
|
|
|
continue;
|
|
|
|
}
|
2016-03-14 17:14:35 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* The option doesn't have a flag, so it takes an argument. */
|
|
|
|
if (*ptr == '\0') {
|
|
|
|
rcfile_error(N_("Option \"%s\" requires an argument"),
|
|
|
|
rcopts[i].name);
|
|
|
|
continue;
|
|
|
|
}
|
2016-03-14 17:14:35 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
option = ptr;
|
|
|
|
if (*option == '"')
|
|
|
|
option++;
|
|
|
|
ptr = parse_argument(ptr);
|
2016-03-14 17:14:35 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
option = mallocstrcpy(NULL, option);
|
2018-07-08 08:45:43 +00:00
|
|
|
|
2018-07-08 08:40:22 +00:00
|
|
|
#ifdef ENABLE_UTF8
|
|
|
|
/* When in a UTF-8 locale, ignore arguments with invalid sequences. */
|
|
|
|
if (using_utf8() && mbstowcs(NULL, option, 0) == (size_t)-1) {
|
2017-12-29 18:27:33 +00:00
|
|
|
rcfile_error(N_("Argument is not a valid multibyte string"));
|
|
|
|
continue;
|
|
|
|
}
|
2018-07-08 08:40:22 +00:00
|
|
|
#endif
|
2017-11-01 18:45:33 +00:00
|
|
|
#ifdef ENABLE_COLOR
|
2017-12-29 18:27:33 +00:00
|
|
|
if (strcasecmp(rcopts[i].name, "titlecolor") == 0)
|
2018-01-15 15:07:24 +00:00
|
|
|
color_combo[TITLE_BAR] = parse_interface_color(option);
|
2017-12-29 18:27:33 +00:00
|
|
|
else if (strcasecmp(rcopts[i].name, "numbercolor") == 0)
|
2018-01-15 15:07:24 +00:00
|
|
|
color_combo[LINE_NUMBER] = parse_interface_color(option);
|
2019-03-03 10:04:19 +00:00
|
|
|
else if (strcasecmp(rcopts[i].name, "stripecolor") == 0)
|
|
|
|
color_combo[GUIDE_STRIPE] = parse_interface_color(option);
|
2017-12-29 18:27:33 +00:00
|
|
|
else if (strcasecmp(rcopts[i].name, "selectedcolor") == 0)
|
2018-01-15 15:07:24 +00:00
|
|
|
color_combo[SELECTED_TEXT] = parse_interface_color(option);
|
2017-12-29 18:27:33 +00:00
|
|
|
else if (strcasecmp(rcopts[i].name, "statuscolor") == 0)
|
2018-01-15 15:07:24 +00:00
|
|
|
color_combo[STATUS_BAR] = parse_interface_color(option);
|
2018-02-20 08:50:54 +00:00
|
|
|
else if (strcasecmp(rcopts[i].name, "errorcolor") == 0)
|
|
|
|
color_combo[ERROR_MESSAGE] = parse_interface_color(option);
|
2017-12-29 18:27:33 +00:00
|
|
|
else if (strcasecmp(rcopts[i].name, "keycolor") == 0)
|
2018-01-15 15:07:24 +00:00
|
|
|
color_combo[KEY_COMBO] = parse_interface_color(option);
|
2017-12-29 18:27:33 +00:00
|
|
|
else if (strcasecmp(rcopts[i].name, "functioncolor") == 0)
|
2018-01-15 15:07:24 +00:00
|
|
|
color_combo[FUNCTION_TAG] = parse_interface_color(option);
|
2017-12-29 18:27:33 +00:00
|
|
|
else
|
2014-05-03 18:24:45 +00:00
|
|
|
#endif
|
2017-10-29 20:08:07 +00:00
|
|
|
#ifdef ENABLE_OPERATINGDIR
|
2017-12-29 18:27:33 +00:00
|
|
|
if (strcasecmp(rcopts[i].name, "operatingdir") == 0)
|
|
|
|
operating_dir = option;
|
|
|
|
else
|
2002-07-19 01:08:59 +00:00
|
|
|
#endif
|
2017-10-31 16:39:30 +00:00
|
|
|
#ifdef ENABLED_WRAPORJUSTIFY
|
2017-12-29 18:27:33 +00:00
|
|
|
if (strcasecmp(rcopts[i].name, "fill") == 0) {
|
2018-10-22 23:01:23 +00:00
|
|
|
if (!parse_num(option, &fill)) {
|
2017-12-29 18:27:33 +00:00
|
|
|
rcfile_error(N_("Requested fill size \"%s\" is invalid"),
|
|
|
|
option);
|
2018-10-22 23:01:23 +00:00
|
|
|
fill = -COLUMNS_FROM_EOL;
|
2019-01-25 18:25:39 +00:00
|
|
|
}
|
2017-12-29 18:27:33 +00:00
|
|
|
free(option);
|
|
|
|
} else
|
2002-07-19 01:08:59 +00:00
|
|
|
#endif
|
2005-11-15 03:17:35 +00:00
|
|
|
#ifndef NANO_TINY
|
2018-12-17 18:57:30 +00:00
|
|
|
if (strcasecmp(rcopts[i].name, "guidestripe") == 0) {
|
|
|
|
if (!parse_num(option, &stripe_column) || stripe_column <= 0) {
|
2019-03-11 07:49:47 +00:00
|
|
|
rcfile_error(N_("Guide column \"%s\" is invalid"), option);
|
2018-12-17 18:57:30 +00:00
|
|
|
stripe_column = 0;
|
|
|
|
}
|
|
|
|
free(option);
|
2019-04-03 16:55:24 +00:00
|
|
|
} else if (strcasecmp(rcopts[i].name, "matchbrackets") == 0) {
|
2019-03-21 18:17:03 +00:00
|
|
|
if (has_blank_char(option)) {
|
2017-12-29 18:27:33 +00:00
|
|
|
rcfile_error(N_("Non-blank characters required"));
|
2019-03-21 18:17:03 +00:00
|
|
|
free(option);
|
2019-04-06 08:03:52 +00:00
|
|
|
} else if (mbstrlen(option) % 2 != 0) {
|
|
|
|
rcfile_error(N_("Even number of characters required"));
|
|
|
|
free(option);
|
2019-03-21 18:17:03 +00:00
|
|
|
} else
|
|
|
|
matchbrackets = option;
|
2017-12-29 18:27:33 +00:00
|
|
|
} else if (strcasecmp(rcopts[i].name, "whitespace") == 0) {
|
2019-04-24 06:49:18 +00:00
|
|
|
if (mbstrlen(option) != 2 || breadth(option) != 2) {
|
2017-12-29 18:27:33 +00:00
|
|
|
rcfile_error(N_("Two single-column characters required"));
|
2019-03-21 18:17:03 +00:00
|
|
|
free(option);
|
2017-12-29 18:27:33 +00:00
|
|
|
} else {
|
2019-03-21 18:17:03 +00:00
|
|
|
whitespace = option;
|
2019-03-21 18:21:45 +00:00
|
|
|
whitelen[0] = parse_mbchar(whitespace, NULL, NULL);
|
|
|
|
whitelen[1] = parse_mbchar(whitespace + whitelen[0], NULL, NULL);
|
2017-12-29 18:27:33 +00:00
|
|
|
}
|
|
|
|
} else
|
2004-05-29 16:47:52 +00:00
|
|
|
#endif
|
2017-10-31 16:34:07 +00:00
|
|
|
#ifdef ENABLE_JUSTIFY
|
2017-12-29 18:27:33 +00:00
|
|
|
if (strcasecmp(rcopts[i].name, "punct") == 0) {
|
2019-03-21 16:42:34 +00:00
|
|
|
if (has_blank_char(option)) {
|
2017-12-29 18:27:33 +00:00
|
|
|
rcfile_error(N_("Non-blank characters required"));
|
2018-05-24 18:43:08 +00:00
|
|
|
free(option);
|
|
|
|
} else
|
|
|
|
punct = option;
|
2017-12-29 18:27:33 +00:00
|
|
|
} else if (strcasecmp(rcopts[i].name, "brackets") == 0) {
|
2019-03-21 16:42:34 +00:00
|
|
|
if (has_blank_char(option)) {
|
2017-12-29 18:27:33 +00:00
|
|
|
rcfile_error(N_("Non-blank characters required"));
|
2018-05-24 18:43:08 +00:00
|
|
|
free(option);
|
|
|
|
} else
|
|
|
|
brackets = option;
|
2017-12-29 18:27:33 +00:00
|
|
|
} else if (strcasecmp(rcopts[i].name, "quotestr") == 0)
|
|
|
|
quotestr = option;
|
|
|
|
else
|
2002-03-03 22:52:52 +00:00
|
|
|
#endif
|
2005-11-15 03:17:35 +00:00
|
|
|
#ifndef NANO_TINY
|
2017-12-29 18:27:33 +00:00
|
|
|
if (strcasecmp(rcopts[i].name, "backupdir") == 0)
|
|
|
|
backup_dir = option;
|
|
|
|
else
|
|
|
|
if (strcasecmp(rcopts[i].name, "wordchars") == 0)
|
|
|
|
word_chars = option;
|
|
|
|
else
|
2004-02-28 16:24:31 +00:00
|
|
|
#endif
|
2017-10-31 18:32:42 +00:00
|
|
|
#ifdef ENABLE_SPELLER
|
2017-12-29 18:27:33 +00:00
|
|
|
if (strcasecmp(rcopts[i].name, "speller") == 0)
|
|
|
|
alt_speller = option;
|
|
|
|
else
|
2005-03-10 20:55:11 +00:00
|
|
|
#endif
|
2017-12-29 18:27:33 +00:00
|
|
|
if (strcasecmp(rcopts[i].name, "tabsize") == 0) {
|
|
|
|
if (!parse_num(option, &tabsize) || tabsize <= 0) {
|
|
|
|
rcfile_error(N_("Requested tab size \"%s\" is invalid"),
|
|
|
|
option);
|
|
|
|
tabsize = -1;
|
|
|
|
}
|
|
|
|
free(option);
|
2018-01-24 09:29:50 +00:00
|
|
|
}
|
2017-12-29 18:27:33 +00:00
|
|
|
}
|
2005-03-10 20:55:11 +00:00
|
|
|
|
2017-11-01 18:45:33 +00:00
|
|
|
#ifdef ENABLE_COLOR
|
2017-12-29 18:27:33 +00:00
|
|
|
if (opensyntax && lastcolor == NULL)
|
|
|
|
rcfile_error(N_("Syntax \"%s\" has no color commands"),
|
|
|
|
live_syntax->name);
|
2006-05-26 12:56:30 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
opensyntax = FALSE;
|
2016-03-11 17:14:30 +00:00
|
|
|
#endif
|
2016-02-28 20:38:14 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
free(buf);
|
|
|
|
fclose(rcstream);
|
|
|
|
lineno = 0;
|
2004-07-30 22:52:44 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
return;
|
2001-04-18 04:28:54 +00:00
|
|
|
}
|
|
|
|
|
2016-11-27 17:21:04 +00:00
|
|
|
/* Read and interpret one of the two nanorc files. */
|
|
|
|
void parse_one_nanorc(void)
|
2001-04-18 04:28:54 +00:00
|
|
|
{
|
2017-12-29 18:27:33 +00:00
|
|
|
FILE *rcstream;
|
2001-04-18 04:28:54 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
/* Don't try to open directories nor devices. */
|
|
|
|
if (!is_good_file(nanorc))
|
|
|
|
return;
|
2006-04-12 21:44:07 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
rcstream = fopen(nanorc, "rb");
|
|
|
|
|
|
|
|
/* If opening the file succeeded, parse it. Otherwise, only
|
|
|
|
* complain if the file actually exists. */
|
|
|
|
if (rcstream != NULL)
|
|
|
|
parse_rcfile(rcstream, FALSE);
|
|
|
|
else if (errno != ENOENT)
|
|
|
|
rcfile_error(N_("Error reading %s: %s"), nanorc, strerror(errno));
|
2016-11-27 17:21:04 +00:00
|
|
|
}
|
|
|
|
|
2017-11-09 19:58:15 +00:00
|
|
|
bool have_nanorc(const char *path, char *name)
|
2017-10-27 21:15:06 +00:00
|
|
|
{
|
2017-12-29 18:27:33 +00:00
|
|
|
if (path == NULL)
|
|
|
|
return FALSE;
|
2017-10-27 21:15:06 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
free(nanorc);
|
|
|
|
nanorc = concatenate(path, name);
|
2017-10-27 21:15:06 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
return is_good_file(nanorc);
|
2017-10-27 21:15:06 +00:00
|
|
|
}
|
|
|
|
|
2016-11-27 17:21:04 +00:00
|
|
|
/* First read the system-wide rcfile, then the user's rcfile. */
|
|
|
|
void do_rcfiles(void)
|
|
|
|
{
|
2017-12-29 18:27:33 +00:00
|
|
|
const char *xdgconfdir;
|
2017-11-09 19:58:15 +00:00
|
|
|
|
2018-09-02 11:02:50 +00:00
|
|
|
/* First process the system-wide nanorc, if there is one. */
|
2017-12-29 18:27:33 +00:00
|
|
|
nanorc = mallocstrcpy(nanorc, SYSCONFDIR "/nanorc");
|
|
|
|
parse_one_nanorc();
|
2002-02-16 20:34:57 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
get_homedir();
|
|
|
|
xdgconfdir = getenv("XDG_CONFIG_HOME");
|
2005-03-10 20:55:11 +00:00
|
|
|
|
2018-09-02 11:02:50 +00:00
|
|
|
/* Now try the to find a nanorc file in the user's home directory or in
|
|
|
|
* the XDG configuration directories, and process the first one found. */
|
2018-04-20 08:47:10 +00:00
|
|
|
if (have_nanorc(homedir, "/" HOME_RC_NAME))
|
2017-12-29 18:27:33 +00:00
|
|
|
parse_one_nanorc();
|
|
|
|
else if (have_nanorc(xdgconfdir, "/nano/" RCFILE_NAME))
|
|
|
|
parse_one_nanorc();
|
|
|
|
else if (have_nanorc(homedir, "/.config/nano/" RCFILE_NAME))
|
|
|
|
parse_one_nanorc();
|
|
|
|
else if (homedir == NULL && xdgconfdir == NULL)
|
|
|
|
rcfile_error(N_("I can't find my home directory! Wah!"));
|
2005-03-04 15:09:55 +00:00
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
check_vitals_mapped();
|
2016-11-27 15:40:54 +00:00
|
|
|
|
2018-08-04 08:40:08 +00:00
|
|
|
#ifdef __linux__
|
|
|
|
/* On a Linux console, don't start nano when there are rcfile errors,
|
|
|
|
* because otherwise these error messages get wiped. */
|
|
|
|
if (on_a_vt && rcfile_with_errors) {
|
|
|
|
fprintf(stderr, _("If needed, use nano with the -I option "
|
|
|
|
"to adjust your nanorc settings.\n"));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-12-29 18:27:33 +00:00
|
|
|
free(nanorc);
|
2001-04-18 04:28:54 +00:00
|
|
|
}
|
|
|
|
|
2017-05-08 17:42:44 +00:00
|
|
|
#endif /* ENABLE_NANORC */
|