rcfile.c 34.6 KB
Newer Older
1
2
/* $Id$ */
/**************************************************************************
3
 *   rcfile.c                                                             *
4
 *                                                                        *
5
6
 *   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,  *
 *   2010, 2011, 2013, 2014 Free Software Foundation, Inc.                *
7
8
 *   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 *
9
 *   the Free Software Foundation; either version 3, or (at your option)  *
10
11
 *   any later version.                                                   *
 *                                                                        *
12
13
14
15
 *   This program 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.                             *
16
17
18
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
 *   along with this program; if not, write to the Free Software          *
19
20
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA            *
 *   02110-1301, USA.                                                     *
21
22
23
 *                                                                        *
 **************************************************************************/

24
#include "proto.h"
25

26
#include <glob.h>
27
#include <stdarg.h>
28
29
30
#include <string.h>
#include <stdio.h>
#include <errno.h>
Chris Allegretta's avatar
Chris Allegretta committed
31
#include <unistd.h>
32
#include <ctype.h>
33

34
#ifndef DISABLE_NANORC
35

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
36
static const rcoption rcopts[] = {
37
    {"boldtext", BOLD_TEXT},
38
39
#ifndef DISABLE_JUSTIFY
    {"brackets", 0},
40
#endif
41
42
    {"const", CONST_UPDATE},  /* deprecated form, remove in 2018 */
    {"constantshow", CONST_UPDATE},
Chris Allegretta's avatar
Chris Allegretta committed
43
#ifndef DISABLE_WRAPJUSTIFY
44
    {"fill", 0},
Chris Allegretta's avatar
Chris Allegretta committed
45
#endif
46
47
#ifndef DISABLE_HISTORIES
    {"historylog", HISTORYLOG},
48
#endif
49
    {"morespace", MORE_SPACE},
50
#ifndef DISABLE_MOUSE
51
    {"mouse", USE_MOUSE},
52
#endif
53
#ifndef DISABLE_MULTIBUFFER
54
55
56
    {"multibuffer", MULTIBUFFER},
#endif
    {"nohelp", NO_HELP},
57
    {"nonewlines", NO_NEWLINES},
Chris Allegretta's avatar
Chris Allegretta committed
58
#ifndef DISABLE_WRAPPING
59
    {"nowrap", NO_WRAP},
Chris Allegretta's avatar
Chris Allegretta committed
60
#endif
61
#ifndef DISABLE_OPERATINGDIR
62
    {"operatingdir", 0},
63
64
#endif
#ifndef DISABLE_HISTORIES
65
66
    {"poslog", POS_HISTORY},  /* deprecated form, remove in 2018 */
    {"positionlog", POS_HISTORY},
67
#endif
Chris Allegretta's avatar
Chris Allegretta committed
68
    {"preserve", PRESERVE},
69
#ifndef DISABLE_JUSTIFY
70
    {"punct", 0},
71
72
    {"quotestr", 0},
#endif
73
    {"rebinddelete", REBIND_DELETE},
74
    {"rebindkeypad", REBIND_KEYPAD},
75
76
77
#ifdef HAVE_REGEX_H
    {"regexp", USE_REGEXP},
#endif
78
#ifndef DISABLE_SPELLER
79
    {"speller", 0},
80
81
82
#endif
    {"suspend", SUSPEND},
    {"tabsize", 0},
83
    {"tempfile", TEMP_FILE},
84
    {"view", VIEW_MODE},
85
#ifndef NANO_TINY
86
    {"allow_insecure_backup", INSECURE_BACKUP},
87
88
89
90
91
92
    {"autoindent", AUTOINDENT},
    {"backup", BACKUP_FILE},
    {"backupdir", 0},
    {"backwards", BACKWARDS_SEARCH},
    {"casesensitive", CASE_SENSITIVE},
    {"cut", CUT_TO_END},
93
    {"justifytrim", JUSTIFY_TRIM},
94
    {"locking", LOCKING},
95
    {"matchbrackets", 0},
96
    {"noconvert", NO_CONVERT},
97
    {"quickblank", QUICK_BLANK},
98
    {"quiet", QUIET},
99
100
    {"smarthome", SMART_HOME},
    {"smooth", SMOOTH_SCROLL},
101
    {"softwrap", SOFTWRAP},
102
    {"tabstospaces", TABS_TO_SPACES},
103
    {"unix", MAKE_IT_UNIX},
104
    {"whitespace", 0},
105
    {"wordbounds", WORD_BOUNDS},
106
107
108
109
110
111
#endif
#ifndef DISABLE_COLOR
    {"titlecolor", 0},
    {"statuscolor", 0},
    {"keycolor", 0},
    {"functioncolor", 0},
112
#endif
113
    {NULL, 0}
114
};
115

116
static bool errors = FALSE;
117
	/* Whether we got any errors while parsing an rcfile. */
118
static size_t lineno = 0;
119
	/* If we did, the line number where the last error occurred. */
120
static char *nanorc = NULL;
121
	/* The path to the rcfile we're parsing. */
122
#ifndef DISABLE_COLOR
123
124
125
static bool opensyntax = FALSE;
	/* 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. */
126
127
128
129
130
static syntaxtype *endsyntax = NULL;
	/* The end of the list of syntaxes. */
static colortype *endcolor = NULL;
	/* The end of the color list for the current syntax. */
#endif
131

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
132
133
134
/* We have an error in some part of the rcfile.  Print the error message
 * on stderr, and then make the user hit Enter to continue starting
 * nano. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
135
void rcfile_error(const char *msg, ...)
136
137
138
{
    va_list ap;

139
140
141
    if (ISSET(QUIET))
	return;

142
    fprintf(stderr, "\n");
143
144
    if (lineno > 0) {
	errors = TRUE;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
145
	fprintf(stderr, _("Error in %s on line %lu: "), nanorc, (unsigned long)lineno);
146
    }
Chris Allegretta's avatar
Chris Allegretta committed
147

148
    va_start(ap, msg);
149
    vfprintf(stderr, _(msg), ap);
150
    va_end(ap);
151
152

    fprintf(stderr, "\n");
153
}
154
#endif /* !DISABLE_NANORC */
155

156
#if !defined(DISABLE_NANORC) || !defined(DISABLE_HISTORIES)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
157
158
159
/* 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. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
160
char *parse_next_word(char *ptr)
161
{
162
    while (!isblank(*ptr) && *ptr != '\0')
163
164
165
	ptr++;

    if (*ptr == '\0')
166
	return ptr;
167

168
169
    /* Null-terminate and advance ptr. */
    *ptr++ = '\0';
170

171
    while (isblank(*ptr))
172
173
174
175
	ptr++;

    return ptr;
}
176
#endif /* !DISABLE_NANORC || !DISABLE_HISTORIES */
177

178
#ifndef DISABLE_NANORC
179
180
181
182
/* 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. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
183
184
char *parse_argument(char *ptr)
{
185
    const char *ptr_save = ptr;
Chris Allegretta's avatar
Chris Allegretta committed
186
187
188
189
190
191
192
193
194
195
196
    char *last_quote = NULL;

    assert(ptr != NULL);

    if (*ptr != '"')
	return parse_next_word(ptr);

    do {
	ptr++;
	if (*ptr == '"')
	    last_quote = ptr;
197
    } while (*ptr != '\0');
Chris Allegretta's avatar
Chris Allegretta committed
198
199
200
201
202
203

    if (last_quote == NULL) {
	if (*ptr == '\0')
	    ptr = NULL;
	else
	    *ptr++ = '\0';
204
	rcfile_error(N_("Argument '%s' has an unterminated \""), ptr_save);
Chris Allegretta's avatar
Chris Allegretta committed
205
206
207
208
209
    } else {
	*last_quote = '\0';
	ptr = last_quote + 1;
    }
    if (ptr != NULL)
210
	while (isblank(*ptr))
Chris Allegretta's avatar
Chris Allegretta committed
211
212
213
214
	    ptr++;
    return ptr;
}

215
#ifndef DISABLE_COLOR
216
/* Parse the next regex string from the line at ptr, and return it. */
217
218
char *parse_next_regex(char *ptr)
{
219
220
221
222
    assert(ptr != NULL);

    /* Continue until the end of the line, or a " followed by a space, a
     * blank character, or \0. */
223
    while ((*ptr != '"' || (!isblank(*(ptr + 1)) &&
224
	*(ptr + 1) != '\0')) && *ptr != '\0')
225
226
	ptr++;

227
228
229
    assert(*ptr == '"' || *ptr == '\0');

    if (*ptr == '\0') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
230
231
	rcfile_error(
		N_("Regex strings must begin and end with a \" character"));
232
	return NULL;
233
    }
234

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
235
    /* Null-terminate and advance ptr. */
236
237
    *ptr++ = '\0';

238
    while (isblank(*ptr))
239
240
241
242
243
	ptr++;

    return ptr;
}

244
245
/* Compile the regular expression regex to see if it's valid.  Return
 * TRUE if it is, or FALSE otherwise. */
246
bool nregcomp(const char *regex, int eflags)
247
{
248
    regex_t preg;
249
    const char *r = fixbounds(regex);
250
    int rc = regcomp(&preg, r, REG_EXTENDED | eflags);
251
252

    if (rc != 0) {
253
	size_t len = regerror(rc, &preg, NULL, 0);
254
255
	char *str = charalloc(len);

256
	regerror(rc, &preg, str, len);
257
	rcfile_error(N_("Bad regex \"%s\": %s"), r, str);
258
259
	free(str);
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
260

261
    regfree(&preg);
262
    return (rc == 0);
263
264
}

265
266
/* Parse the next syntax string from the line at ptr, and add it to the
 * global list of color syntaxes. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
267
void parse_syntax(char *ptr)
268
{
269
270
    char *nameptr;
	/* A pointer to what should be the name of the syntax. */
271

272
273
    opensyntax = FALSE;

274
    assert(ptr != NULL);
275

276
277
278
    /* Check that the syntax name is not empty. */
    if (*ptr == '\0' || (*ptr == '"' &&
			(*(ptr + 1) == '\0' || *(ptr + 1) == '"'))) {
279
	rcfile_error(N_("Missing syntax name"));
280
	return;
281
    }
282

283
284
285
286
287
288
    nameptr = ++ptr;
    ptr = parse_next_word(ptr);

    /* Check that the name starts and ends with a double quote. */
    if (*(nameptr - 1) != '\x22' || nameptr[strlen(nameptr) - 1] != '\x22') {
	rcfile_error(N_("A syntax name must be quoted"));
289
	return;
290
    }
291

292
293
    /* Strip the end quote. */
    nameptr[strlen(nameptr) - 1] = '\0';
294

295
296
297
298
299
300
    /* Redefining the "none" syntax is not allowed. */
    if (strcmp(nameptr, "none") == 0) {
	rcfile_error(N_("The \"none\" syntax is reserved"));
	return;
    }

Chris Allegretta's avatar
Chris Allegretta committed
301
302
    if (syntaxes == NULL) {
	syntaxes = (syntaxtype *)nmalloc(sizeof(syntaxtype));
303
	endsyntax = syntaxes;
Chris Allegretta's avatar
Chris Allegretta committed
304
    } else {
305
306
	endsyntax->next = (syntaxtype *)nmalloc(sizeof(syntaxtype));
	endsyntax = endsyntax->next;
307
#ifdef DEBUG
308
	fprintf(stderr, "Adding new syntax after first one\n");
309
#endif
Chris Allegretta's avatar
Chris Allegretta committed
310
    }
311

312
    endsyntax->name = mallocstrcpy(NULL, nameptr);
313
    endsyntax->extensions = NULL;
314
    endsyntax->headers = NULL;
315
    endsyntax->magics = NULL;
316
    endsyntax->linter = NULL;
317
    endsyntax->formatter = NULL;
318
319
320
321
    endsyntax->color = NULL;
    endcolor = NULL;
    endsyntax->nmultis = 0;
    endsyntax->next = NULL;
322

323
324
    opensyntax = TRUE;

325
#ifdef DEBUG
326
    fprintf(stderr, "Starting a new syntax type: \"%s\"\n", nameptr);
327
328
#endif

329
    /* The default syntax should have no associated extensions. */
330
    if (strcmp(endsyntax->name, "default") == 0 && *ptr != '\0') {
331
332
	rcfile_error(
		N_("The \"default\" syntax must take no extensions"));
333
334
335
	return;
    }

336
337
338
    /* If there seem to be extension regexes, pick them up. */
    if (*ptr != '\0')
	grab_and_store(ptr, "extension", &endsyntax->extensions);
339
}
340
#endif /* !DISABLE_COLOR */
341

342
343
344
345
346
347
348
/* 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 ||
349
#ifndef NANO_TINY
350
	func == do_prev_word_void || func == do_next_word_void ||
351
#endif
352
353
354
355
356
357
358
359
	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;
}

360
/* Bind or unbind a key combo, to or from a function. */
361
void parse_binding(char *ptr, bool dobind)
362
363
{
    char *keyptr = NULL, *keycopy = NULL, *funcptr = NULL, *menuptr = NULL;
364
    sc *s, *newsc = NULL;
365
    int menu;
366
367
368

    assert(ptr != NULL);

369
370
371
372
#ifdef DEBUG
    fprintf(stderr, "Starting the rebinding code...\n");
#endif

373
374
375
376
377
378
379
380
    if (*ptr == '\0') {
	rcfile_error(N_("Missing key name"));
	return;
    }

    keyptr = ptr;
    ptr = parse_next_word(ptr);
    keycopy = mallocstrcpy(NULL, keyptr);
381
382
383

    if (strlen(keycopy) < 2) {
	rcfile_error(N_("Key name is too short"));
Benno Schulenberg's avatar
Benno Schulenberg committed
384
	goto free_copy;
385
386
387
388
389
390
391
392
393
394
    }

    /* 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"));
Benno Schulenberg's avatar
Benno Schulenberg committed
395
	    goto free_copy;
396
397
	}
    }
398

399
400
401
402
403
    /* 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') {
404
	rcfile_error(N_("Key name must begin with \"^\", \"M\", or \"F\""));
Benno Schulenberg's avatar
Benno Schulenberg committed
405
	goto free_copy;
406
407
408
    } else if (keycopy[0] == '^' && (keycopy[1] < 64 || keycopy[1] > 127)) {
	rcfile_error(N_("Key name %s is invalid"), keycopy);
	goto free_copy;
409
410
    }

411
412
413
    if (dobind) {
	funcptr = ptr;
	ptr = parse_next_word(ptr);
414

415
	if (funcptr[0] == '\0') {
416
	    rcfile_error(N_("Must specify a function to bind the key to"));
Benno Schulenberg's avatar
Benno Schulenberg committed
417
	    goto free_copy;
418
	}
419
420
421
422
423
    }

    menuptr = ptr;
    ptr = parse_next_word(ptr);

424
    if (menuptr[0] == '\0') {
425
	/* TRANSLATORS: Do not translate the word "all". */
426
	rcfile_error(N_("Must specify a menu (or \"all\") in which to bind/unbind the key"));
Benno Schulenberg's avatar
Benno Schulenberg committed
427
	goto free_copy;
428
    }
429
430

    if (dobind) {
431
	newsc = strtosc(funcptr);
432
	if (newsc == NULL) {
433
	    rcfile_error(N_("Cannot map name \"%s\" to a function"), funcptr);
Benno Schulenberg's avatar
Benno Schulenberg committed
434
	    goto free_copy;
435
	}
436
437
    }

438
439
440
    menu = strtomenu(menuptr);
    if (menu < 1) {
	rcfile_error(N_("Cannot map name \"%s\" to a menu"), menuptr);
Benno Schulenberg's avatar
Benno Schulenberg committed
441
	goto free_copy;
442
443
    }

444
#ifdef DEBUG
445
    if (dobind)
446
447
	fprintf(stderr, "newsc address is now %ld, assigned func = %ld, menu = %x\n",
	    (long)&newsc, (long)newsc->scfunc, menu);
448
    else
449
	fprintf(stderr, "unbinding \"%s\" from menu %x\n", keycopy, menu);
450
451
#endif

452
    if (dobind) {
453
454
455
456
457
458
459
460
	subnfunc *f;
	int mask = 0;

	/* 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;

461
462
463
464
	/* Handle the special case of the toggles. */
	if (newsc->scfunc == do_toggle_void)
	    mask = MMAIN;

465
466
467
468
469
470
471
472
473
	/* 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);
Benno Schulenberg's avatar
Benno Schulenberg committed
474
	    goto free_copy;
475
476
	}

477
	newsc->keystr = keycopy;
478
	newsc->menus = menu;
479
480
	newsc->type = strtokeytype(newsc->keystr);
	assign_keyinfo(newsc);
481

482
483
	/* Do not allow rebinding the equivalent of the Escape key. */
	if (newsc->type == META && newsc->seq == 91) {
484
485
	    rcfile_error(N_("Sorry, keystroke \"%s\" may not be rebound"), newsc->keystr);
	    free(newsc);
Benno Schulenberg's avatar
Benno Schulenberg committed
486
	    goto free_copy;
487
	}
488
489
490
491
#ifdef DEBUG
	fprintf(stderr, "s->keystr = \"%s\"\n", newsc->keystr);
	fprintf(stderr, "s->seq = \"%d\"\n", newsc->seq);
#endif
492
    }
493

494
    /* Now find and delete any existing same shortcut in the menu(s). */
495
    for (s = sclist; s != NULL; s = s->next) {
496
	if ((s->menus & menu) && !strcmp(s->keystr, keycopy)) {
497
#ifdef DEBUG
498
	    fprintf(stderr, "deleting entry from among menus %x\n", s->menus);
499
#endif
500
	    s->menus &= ~menu;
501
502
	}
    }
503
504

    if (dobind) {
505
506
507
	/* If this is a toggle, copy its sequence number. */
	if (newsc->scfunc == do_toggle_void) {
	    for (s = sclist; s != NULL; s = s->next)
508
		if (s->scfunc == do_toggle_void && s->toggle == newsc->toggle)
509
510
511
		    newsc->ordinal = s->ordinal;
	} else
	    newsc->ordinal = 0;
512
513
514
	/* Add the new shortcut at the start of the list. */
	newsc->next = sclist;
	sclist = newsc;
Benno Schulenberg's avatar
Benno Schulenberg committed
515
516
517
518
519
	return;
    }

  free_copy:
    free(keycopy);
520
521
}

522

523
#ifndef DISABLE_COLOR
524
/* Read and parse additional syntax files. */
525
static void _parse_include(char *file)
526
527
528
529
{
    struct stat rcinfo;
    FILE *rcstream;

530
531
532
    /* 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). */
533
534

    /* Don't open directories, character files, or block files. */
535
    if (stat(file, &rcinfo) != -1) {
536
537
538
539
	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") :
540
		_("\"%s\" is a device file"), file);
541
542
543
544
	}
    }

    /* Open the new syntax file. */
545
546
    if ((rcstream = fopen(file, "rb")) == NULL) {
	rcfile_error(_("Error reading %s: %s"), file,
547
		strerror(errno));
548
	return;
549
550
551
552
    }

    /* 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. */
553
    nanorc = file;
554
555
    lineno = 0;

556
#ifdef DEBUG
557
    fprintf(stderr, "Parsing file \"%s\"\n", file);
558
559
#endif

560
    parse_rcfile(rcstream, TRUE);
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
}

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));
    }
584

585
586
587
    globfree(&files);
    free(expanded);

588
589
590
591
592
593
    /* We're done with the new syntax file.  Restore the original
     * filename and line number position. */
    nanorc = nanorc_save;
    lineno = lineno_save;
}

594
595
/* Return the short value corresponding to the color named in colorname,
 * and set bright to TRUE if that color is bright. */
596
short color_to_short(const char *colorname, bool *bright)
597
{
598
    short mcolor = -1;
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632

    assert(colorname != NULL && bright != NULL);

    if (strncasecmp(colorname, "bright", 6) == 0) {
	*bright = TRUE;
	colorname += 6;
    }

    if (strcasecmp(colorname, "green") == 0)
	mcolor = COLOR_GREEN;
    else if (strcasecmp(colorname, "red") == 0)
	mcolor = COLOR_RED;
    else if (strcasecmp(colorname, "blue") == 0)
	mcolor = COLOR_BLUE;
    else if (strcasecmp(colorname, "white") == 0)
	mcolor = COLOR_WHITE;
    else if (strcasecmp(colorname, "yellow") == 0)
	mcolor = COLOR_YELLOW;
    else if (strcasecmp(colorname, "cyan") == 0)
	mcolor = COLOR_CYAN;
    else if (strcasecmp(colorname, "magenta") == 0)
	mcolor = COLOR_MAGENTA;
    else if (strcasecmp(colorname, "black") == 0)
	mcolor = COLOR_BLACK;
    else
	rcfile_error(N_("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"
		"for foreground colors."), colorname);

    return mcolor;
}

633
634
635
/* Parse the color string in the line at ptr, and add it to the current
 * file's associated colors.  If icase is TRUE, treat the color string
 * as case insensitive. */
636
void parse_colors(char *ptr, bool icase)
637
{
638
    short fg, bg;
639
    bool bright = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
640
    char *fgstr;
641

642
    assert(ptr != NULL);
643

644
    if (!opensyntax) {
645
	rcfile_error(
646
		N_("Cannot add a color command without a syntax command"));
647
648
649
	return;
    }

650
    if (*ptr == '\0') {
651
	rcfile_error(N_("Missing color name"));
652
	return;
653
654
    }

655
656
    fgstr = ptr;
    ptr = parse_next_word(ptr);
657
658
    if (!parse_color_names(fgstr, &fg, &bg, &bright))
	return;
659

660
    if (*ptr == '\0') {
661
	rcfile_error(N_("Missing regex string"));
662
663
664
	return;
    }

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
665
    /* Now for the fun part.  Start adding regexes to individual strings
666
667
     * in the colorstrings array, woo! */
    while (ptr != NULL && *ptr != '\0') {
668
	colortype *newcolor;
669
	    /* The container for a color plus its regexes. */
670
	bool cancelled = FALSE;
671
	    /* The start expression was bad. */
672
673
	bool expectend = FALSE;
	    /* Do we expect an end= line? */
674

675
	if (strncasecmp(ptr, "start=", 6) == 0) {
676
	    ptr += 6;
677
	    expectend = TRUE;
678
679
680
	}

	if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
681
682
	    rcfile_error(
		N_("Regex strings must begin and end with a \" character"));
683
	    ptr = parse_next_regex(ptr);
684
685
	    continue;
	}
686

687
	fgstr = ++ptr;
688
	ptr = parse_next_regex(ptr);
689
690
691
	if (ptr == NULL)
	    break;

692
693
694
	/* Save the starting regex string if it's valid, and set up the
	 * color information. */
	if (nregcomp(fgstr, icase ? REG_ICASE : 0)) {
695
696
	    newcolor = (colortype *)nmalloc(sizeof(colortype));

697
698
699
	    newcolor->fg = fg;
	    newcolor->bg = bg;
	    newcolor->bright = bright;
700
	    newcolor->icase = icase;
701
702
703
704

	    newcolor->start_regex = mallocstrcpy(NULL, fgstr);
	    newcolor->start = NULL;

705
	    newcolor->end_regex = NULL;
706
	    newcolor->end = NULL;
707

708
	    newcolor->next = NULL;
709

710
711
	    if (endcolor == NULL) {
		endsyntax->color = newcolor;
712
#ifdef DEBUG
713
		fprintf(stderr, "Starting a new colorstring for fg %hd, bg %hd\n", fg, bg);
714
#endif
715
	    } else {
Chris Allegretta's avatar
Chris Allegretta committed
716
#ifdef DEBUG
717
		fprintf(stderr, "Adding new entry for fg %hd, bg %hd\n", fg, bg);
Chris Allegretta's avatar
Chris Allegretta committed
718
#endif
719
720
		/* Need to recompute endcolor now so we can extend
		 * colors to syntaxes. */
721
722
		for (endcolor = endsyntax->color; endcolor->next != NULL;)
		    endcolor = endcolor->next;
723
		endcolor->next = newcolor;
724
	    }
725

726
	    endcolor = newcolor;
727
	} else
728
	    cancelled = TRUE;
Chris Allegretta's avatar
Chris Allegretta committed
729

730
	if (expectend) {
731
	    if (ptr == NULL || strncasecmp(ptr, "end=", 4) != 0) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
732
733
		rcfile_error(
			N_("\"start=\" requires a corresponding \"end=\""));
734
735
736
737
		return;
	    }
	    ptr += 4;
	    if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
738
739
		rcfile_error(
			N_("Regex strings must begin and end with a \" character"));
740
		continue;
741
	    }
742

743
	    fgstr = ++ptr;
744
	    ptr = parse_next_regex(ptr);
745
746
	    if (ptr == NULL)
		break;
747

748
749
	    /* If the start regex was invalid, skip past the end regex
	     * to stay in sync. */
750
751
	    if (cancelled)
		continue;
752

753
754
755
	    /* If it's valid, save the ending regex string. */
	    if (nregcomp(fgstr, icase ? REG_ICASE : 0))
		newcolor->end_regex = mallocstrcpy(NULL, fgstr);
756

757
	    /* Lame way to skip another static counter. */
758
759
	    newcolor->id = endsyntax->nmultis;
	    endsyntax->nmultis++;
Chris Allegretta's avatar
Chris Allegretta committed
760
	}
761
    }
762
}
763

764
765
766
767
768
769
/* Parse the color name, or pair of color names, in combostr. */
bool parse_color_names(char *combostr, short *fg, short *bg, bool *bright)
{
    bool no_fgcolor = FALSE;

    if (combostr == NULL)
770
	return FALSE;
771
772
773
774
775
776
777
778
779
780
781
782
783

    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);
784
	    return FALSE;
785
786
787
788
789
790
791
792
793
794
	}
	*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)
795
	    return FALSE;
796
797
798
    } else
	*fg = -1;

799
    return TRUE;
800
801
}

802
803
804
805
/* Read regex strings enclosed in double quotes from the line pointed at
 * by ptr, and store them quoteless in the passed storage place. */
void grab_and_store(char *ptr, const char *kind, regexlisttype **storage)
{
806
    regexlisttype *lastthing;
807

808
    if (!opensyntax) {
809
	rcfile_error(
810
		N_("A '%s' command requires a preceding 'syntax' command"), kind);
811
812
813
814
	return;
    }

    if (*ptr == '\0') {
815
	rcfile_error(N_("Missing regex string after '%s' command"), kind);
816
817
818
	return;
    }

819
820
821
822
823
824
    lastthing = *storage;

    /* If there was an earlier command, go to the last of those regexes. */
    while (lastthing != NULL && lastthing->next != NULL)
	lastthing = lastthing->next;

825
    /* Now gather any valid regexes and add them to the linked list. */
826
827
    while (*ptr != '\0') {
	const char *regexstring;
828
	regexlisttype *newthing;
829
830
831
832

	if (*ptr != '"') {
	    rcfile_error(
		N_("Regex strings must begin and end with a \" character"));
833
	    return;
834
835
	}

836
	regexstring = ++ptr;
837
838
	ptr = parse_next_regex(ptr);
	if (ptr == NULL)
839
	    return;
840

841
	/* If the regex string is malformed, skip it. */
842
	if (!nregcomp(regexstring, REG_NOSUB))
843
	    continue;
844

845
846
847
848
	/* 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;
849

850
851
852
853
	if (lastthing == NULL)
	    *storage = newthing;
	else
	    lastthing->next = newthing;
854

855
	lastthing = newthing;
856
857
    }
}
858

859
860
861
862
863
864
/* Parse the header-line regexes that may influence the choice of syntax. */
void parse_header_exp(char *ptr)
{
    grab_and_store(ptr, "header", &endsyntax->headers);
}

865
#ifdef HAVE_LIBMAGIC
866
867
868
/* Parse the magic regexes that may influence the choice of syntax. */
void parse_magic_exp(char *ptr)
{
869
    grab_and_store(ptr, "magic", &endsyntax->magics);
870
}
871
#endif /* HAVE_LIBMAGIC */
872

873
/* Parse the linter requested for this syntax. */
874
875
876
877
void parse_linter(char *ptr)
{
    assert(ptr != NULL);

878
    if (!opensyntax) {
879
880
881
882
883
884
885
886
887
888
	rcfile_error(
		N_("Cannot add a linter without a syntax command"));
	return;
    }

    if (*ptr == '\0') {
	rcfile_error(N_("Missing linter command"));
	return;
    }

889
    free(endsyntax->linter);
890

Benno Schulenberg's avatar
Benno Schulenberg committed
891
    /* Let them unset the linter by using "". */
892
893
894
    if (!strcmp(ptr, "\"\""))
	endsyntax->linter = NULL;
    else
895
	endsyntax->linter = mallocstrcpy(NULL, ptr);
896
}
897

898
#ifndef DISABLE_SPELLER
899
/* Parse the formatter requested for this syntax. */
900
901
902
903
void parse_formatter(char *ptr)
{
    assert(ptr != NULL);

904
    if (!opensyntax) {
905
906
907
908
909
910
911
912
913
914
	rcfile_error(
		N_("Cannot add formatter without a syntax command"));
	return;
    }

    if (*ptr == '\0') {
	rcfile_error(N_("Missing formatter command"));
	return;
    }

915
    free(endsyntax->formatter);
916
917
918
919
920

    /* Let them unset the formatter by using "". */
    if (!strcmp(ptr, "\"\""))
	endsyntax->formatter = NULL;
    else
921
	endsyntax->formatter = mallocstrcpy(NULL, ptr);
922
}
923
#endif /* !DISABLE_SPELLER */
924
#endif /* !DISABLE_COLOR */
925

926
/* Check whether the user has unmapped every shortcut for a
927
 * sequence we consider 'vital', like the exit function. */
928
929
930
931
932
static void check_vitals_mapped(void)
{
    subnfunc *f;
    int v;
#define VITALS 5
933
    void (*vitals[VITALS])(void) = { do_exit, do_exit, do_cancel, do_cancel, do_cancel };
934
935
936
    int inmenus[VITALS] = { MMAIN, MHELP, MWHEREIS, MREPLACE, MGOTOLINE };

    for  (v = 0; v < VITALS; v++) {
937
938
939
940
941
	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 "
942
				     "\"%s\".  Exiting.\n"), f->desc);
943
		    fprintf(stderr, _("If needed, use nano with the -I option "
944
				     "to adjust your nanorc settings.\n"));
945
946
947
948
949
		     exit(1);
		}
		break;
	    }
	}
950
951
952
    }
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
953
954
955
/* Parse the rcfile, once it has been opened successfully at rcstream,
 * and close it afterwards.  If syntax_only is TRUE, only allow the file
 * to contain color syntax commands: syntax, color, and icolor. */
956
void parse_rcfile(FILE *rcstream
957
#ifndef DISABLE_COLOR
958
959
960
	, bool syntax_only
#endif
	)
961
{
962
963
    char *buf = NULL;
    ssize_t len;
964
    size_t n = 0;
965
#ifndef DISABLE_COLOR
966
967
    syntaxtype *end_syn_save = NULL;
#endif
968
969
970

    while ((len = getline(&buf, &n, rcstream)) > 0) {
	char *ptr, *keyword, *option;
971
972
	int set = 0;
	size_t i;
973

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
974
	/* Ignore the newline. */
975
976
	if (buf[len - 1] == '\n')
	    buf[len - 1] = '\0';
977
978
979

	lineno++;
	ptr = buf;
980
	while (isblank(*ptr))
981
982
	    ptr++;

983
984
985
	/* If we have a blank line or a comment, skip to the next
	 * line. */
	if (*ptr == '\0' || *ptr == '#')
986
987
	    continue;

988
	/* Otherwise, skip to the next space. */
989
990
991
	keyword = ptr;
	ptr = parse_next_word(ptr);

992
#ifndef DISABLE_COLOR
993
994
	/* Handle extending first... */
	if (strcasecmp(keyword, "extendsyntax") == 0) {
995
	    syntaxtype *sint;
996
997
998
	    char *syntaxname = ptr;

	    ptr = parse_next_word(ptr);
999
1000
1001

	    for (sint = syntaxes; sint != NULL; sint = sint->next)
		if (!strcmp(sint->name, syntaxname))
1002
1003
		    break;

1004
1005
1006
	    if (sint == NULL) {
		rcfile_error(N_("Could not find syntax \"%s\" to extend"),
				syntaxname);
1007
		opensyntax = FALSE;
1008
1009
1010
		continue;
	    } else {
		end_syn_save = endsyntax;
1011
1012
		endsyntax = sint;
		opensyntax = TRUE;
1013
1014
1015
1016
		keyword = ptr;
		ptr = parse_next_word(ptr);
	    }
	}
1017
#endif
1018

1019
	/* Try to parse the keyword. */
1020
	if (strcasecmp(keyword, "set") == 0) {
1021
#ifndef DISABLE_COLOR
1022
1023
	    if (syntax_only)
		rcfile_error(
1024
			N_("Command \"%s\" not allowed in included file"),
1025
1026
1027
1028
1029
			keyword);
	    else
#endif
		set = 1;
	} else if (strcasecmp(keyword, "unset") == 0) {
1030
#ifndef DISABLE_COLOR
1031
1032
	    if (syntax_only)
		rcfile_error(
1033
			N_("Command \"%s\" not allowed in included file"),
1034
1035
1036
1037
1038
			keyword);
	    else
#endif
		set = -1;
	}
1039
#ifndef DISABLE_COLOR
1040
1041
1042
	else if (strcasecmp(keyword, "include") == 0) {
	    if (syntax_only)
		rcfile_error(
1043
			N_("Command \"%s\" not allowed in included file"),
1044
1045
1046
			keyword);
	    else
		parse_include(ptr);
1047
1048
	} else if (strcasecmp(keyword, "syntax") == 0) {
	    if (endsyntax != NULL && endcolor == NULL)
1049
		rcfile_error(N_("Syntax \"%s\" has no color commands"),
1050
			endsyntax->name);
Chris Allegretta's avatar
Chris Allegretta committed
1051
	    parse_syntax(ptr);
1052
	}
1053
	else if (strcasecmp(keyword, "magic") == 0)
1054
#ifdef HAVE_LIBMAGIC
1055
	    parse_magic_exp(ptr);
1056
1057
1058
#else
	    ;
#endif
1059
	else if (strcasecmp(keyword, "header") == 0)
1060
	    parse_header_exp(ptr);
1061
	else if (strcasecmp(keyword, "color") == 0)
1062
1063
1064
	    parse_colors(ptr, FALSE);
	else if (strcasecmp(keyword, "icolor") == 0)
	    parse_colors(ptr, TRUE);
1065
1066
	else if (strcasecmp(keyword, "linter") == 0)
	    parse_linter(ptr);
1067
	else if (strcasecmp(keyword, "formatter") == 0)
1068
#ifndef DISABLE_SPELLER
1069
	    parse_formatter(ptr);
1070
1071
1072
#else
	    ;
#endif
1073
#endif /* !DISABLE_COLOR */
1074
	else if (strcasecmp(keyword, "bind") == 0)
1075
	    parse_binding(ptr, TRUE);
1076
	else if (strcasecmp(keyword, "unbind") == 0)
1077
	    parse_binding(ptr, FALSE);
1078
	else
1079
	    rcfile_error(N_("Command \"%s\" not understood"), keyword);
1080

1081
1082
1083
#ifndef DISABLE_COLOR
	/* If we temporarily reset endsyntax to allow extending,
	 * restore the value here. */
1084
1085
1086
	if (end_syn_save != NULL) {
	    endsyntax = end_syn_save;
	    end_syn_save = NULL;
1087
	    opensyntax = FALSE;
1088
1089
1090
	}
#endif

1091
1092
1093
1094
	if (set == 0)
	    continue;

	if (*ptr == '\0') {
1095
	    rcfile_error(N_("Missing option"));
1096
1097
1098
1099
1100
1101
	    continue;
	}

	option = ptr;
	ptr = parse_next_word(ptr);

1102
1103
	for (i = 0; rcopts[i].name != NULL; i++) {
	    if (strcasecmp(option, rcopts[i].name) == 0) {
1104
#ifdef DEBUG
1105
1106
1107
1108
1109
1110
1111
1112
1113
		fprintf(stderr, "parse_rcfile(): name = \"%s\"\n", rcopts[i].name);
#endif
		if (set == 1) {
		    if (rcopts[i].flag != 0)
			/* This option has a flag, so it doesn't take an
			 * argument. */
			SET(rcopts[i].flag);
		    else {
			/* This option doesn't have a flag, so it takes
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1114
			 * an argument. */
1115
			if (*ptr == '\0') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1116
			    rcfile_error(
1117
				N_("Option \"%s\" requires an argument"),
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1118
				rcopts[i].name);
1119
1120
1121
1122
1123
1124
			    break;
			}
			option = ptr;
			if (*option == '"')
			    option++;
			ptr = parse_argument(ptr);
1125

1126
			option = mallocstrcpy(NULL, option);
Chris Allegretta's avatar
Chris Allegretta committed
1127
#ifdef DEBUG
1128
			fprintf(stderr, "option = \"%s\"\n", option);
Chris Allegretta's avatar
Chris Allegretta committed
1129
#endif
1130
1131
1132
1133
1134
1135
1136
1137
1138

			/* Make sure option is a valid multibyte
			 * string. */
			if (!is_valid_mbstring(option)) {
			    rcfile_error(
				N_("Option is not a valid multibyte string"));
			    break;
			}

1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
#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
Chris Allegretta's avatar
Chris Allegretta committed
1150
#ifndef DISABLE_OPERATINGDIR
1151
			if (strcasecmp(rcopts[i].name, "operatingdir") == 0)
1152
			    operating_dir = option;
1153
			else
Chris Allegretta's avatar
Chris Allegretta committed
1154
#endif
1155
#ifndef DISABLE_WRAPJUSTIFY
1156
1157
			if (strcasecmp(rcopts[i].name, "fill") == 0) {
			    if (!parse_num(option, &wrap_at)) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1158
				rcfile_error(
1159
					N_("Requested fill size \"%s\" is invalid"),
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1160
					option);
1161
				wrap_at = -CHARS_FROM_EOL;
1162
1163
			    } else
				free(option);
1164
			} else
Chris Allegretta's avatar
Chris Allegretta committed
1165
#endif
1166
#ifndef NANO_TINY
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
			if (strcasecmp(rcopts[i].name,
				"matchbrackets") == 0) {
			    matchbrackets = option;
			    if (has_blank_mbchars(matchbrackets)) {
				rcfile_error(
					N_("Non-blank characters required"));
				free(matchbrackets);
				matchbrackets = NULL;
			    }
			} else if (strcasecmp(rcopts[i].name,
				"whitespace") == 0) {
1178
			    whitespace = option;
1179
1180
			    if (mbstrlen(whitespace) != 2 ||
				strlenpt(whitespace) != 2) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1181
1182
				rcfile_error(
					N_("Two single-column characters required"));
1183
1184
				free(whitespace);
				whitespace = NULL;
1185
1186
1187
			    } else {
				whitespace_len[0] =
					parse_mbchar(whitespace, NULL,
1188
					NULL);
1189
1190
				whitespace_len[1] =
					parse_mbchar(whitespace +
1191
					whitespace_len[0], NULL, NULL);
1192
1193
			    }
			} else
1194
#endif
1195
#ifndef DISABLE_JUSTIFY
1196
			if (strcasecmp(rcopts[i].name, "punct") == 0) {
1197
			    punct = option;
1198
			    if (has_blank_mbchars(punct)) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1199
				rcfile_error(
1200
					N_("Non-blank characters required"));
1201
1202
1203
				free(punct);
				punct = NULL;
			    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1204
1205
			} else if (strcasecmp(rcopts[i].name,
				"brackets") == 0) {
1206
			    brackets = option;
1207
			    if (has_blank_mbchars(brackets)) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1208
				rcfile_error(
1209
					N_("Non-blank characters required"));
1210
1211
1212
				free(brackets);
				brackets = NULL;
			    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1213
1214
			} else if (strcasecmp(rcopts[i].name,
				"quotestr") == 0)
1215
			    quotestr = option;
1216
			else
1217
#endif
1218
#ifndef NANO_TINY
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1219
1220
			if (strcasecmp(rcopts[i].name,
				"backupdir") == 0)
1221
			    backup_dir = option;
1222
			else
1223
#endif
1224
#ifndef DISABLE_SPELLER
1225
			if (strcasecmp(rcopts[i].name, "speller") == 0)
1226
			    alt_speller = option;
1227
1228
			else
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1229
1230
1231
1232
1233
			if (strcasecmp(rcopts[i].name,
				"tabsize") == 0) {
			    if (!parse_num(option, &tabsize) ||
				tabsize <= 0) {
				rcfile_error(
1234
					N_("Requested tab size \"%s\" is invalid"),
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1235
					option);
1236
				tabsize = -1;
1237
1238
			    } else
				free(option);
1239
			} else
1240
1241
			    assert(FALSE);
		    }
1242
#ifdef DEBUG
1243
		    fprintf(stderr, "flag = %ld\n", rcopts[i].flag);
1244
#endif
1245
1246
1247
		} else if (rcopts[i].flag != 0)
		    UNSET(rcopts[i].flag);
		else
1248
		    rcfile_error(N_("Cannot unset option \"%s\""),
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1249
			rcopts[i].name);
1250
		break;
1251
1252
	    }
	}
1253
	if (rcopts[i].name == NULL)
1254
	    rcfile_error(N_("Unknown option \"%s\""), option);
1255
    }
1256

1257
#ifndef DISABLE_COLOR
1258
    if (endsyntax != NULL && endcolor == NULL)
1259
	rcfile_error(N_("Syntax \"%s\" has no color commands"),
1260
		endsyntax->name);
1261
#endif
1262

1263
1264
    opensyntax = FALSE;

Chris Allegretta's avatar
Chris Allegretta committed
1265
    free(buf);
1266
1267
    fclose(rcstream);
    lineno = 0;
1268

Chris Allegretta's avatar
Chris Allegretta committed
1269
    check_vitals_mapped();
1270
1271
1272
    return;
}

1273
/* The main rcfile function.  It tries to open the system-wide rcfile,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1274
 * followed by the current user's rcfile. */
1275
1276
void do_rcfile(void)
{
1277
    struct stat rcinfo;
1278
1279
    FILE *rcstream;

1280
    nanorc = mallocstrcpy(nanorc, SYSCONFDIR "/nanorc");
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290

    /* Don't open directories, character files, or block files. */
    if (stat(nanorc, &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"), nanorc);
    }

1291
1292
1293
1294
#ifdef DEBUG
    fprintf(stderr, "Parsing file \"%s\"\n", nanorc);
#endif

1295
    /* Try to open the system-wide nanorc. */
1296
    rcstream = fopen(nanorc, "rb");
1297
    if (rcstream != NULL)
1298
	parse_rcfile(rcstream
1299
#ifndef DISABLE_COLOR
1300
1301
1302
		, FALSE
#endif
		);
1303

1304
#ifdef DISABLE_ROOTWRAPPING
1305
    /* We've already read SYSCONFDIR/nanorc, if it's there.  If we're
1306
1307
     * root, and --disable-wrapping-as-root is used, turn wrapping off
     * now. */
1308
1309
1310
    if (geteuid() == NANO_ROOT_UID)
	SET(NO_WRAP);
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1311

1312
    get_homedir();
1313

1314
    if (homedir == NULL)
1315
	rcfile_error(N_("I can't find my home directory!  Wah!"));
1316
    else {
1317
1318
1319
1320
1321
#ifndef RCFILE_NAME
#define RCFILE_NAME ".nanorc"
#endif
	nanorc = charealloc(nanorc, strlen(homedir) + strlen(RCFILE_NAME) + 2);
	sprintf(nanorc, "%s/%s", homedir, RCFILE_NAME);
1322

1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
	/* Don't open directories, character files, or block files. */
	if (stat(nanorc, &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"), nanorc);
	}

	/* Try to open the current user's nanorc. */
	rcstream = fopen(nanorc, "rb");
1334
	if (rcstream == NULL) {
1335
1336
	    /* Don't complain about the file's not existing. */
	    if (errno != ENOENT)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1337
1338
		rcfile_error(N_("Error reading %s: %s"), nanorc,
			strerror(errno));
1339
	} else
1340
	    parse_rcfile(rcstream
1341
#ifndef DISABLE_COLOR
1342
1343
1344
		, FALSE
#endif
		);
1345
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1346

1347
1348
    free(nanorc);
    nanorc = NULL;
1349

1350
1351
1352
1353
1354
1355
1356
    if (errors && !ISSET(QUIET)) {
	errors = FALSE;
	fprintf(stderr,
		_("\nPress Enter to continue starting nano.\n"));
	while (getchar() != '\n')
	    ;
    }
1357
1358
}

1359
#endif /* !DISABLE_NANORC */