rcfile.c 34.1 KB
Newer Older
1
/**************************************************************************
2
 *   rcfile.c  --  This file is part of GNU nano.                         *
3
 *                                                                        *
4
 *   Copyright (C) 2001-2011, 2013-2017 Free Software Foundation, Inc.    *
5
6
7
 *   Copyright (C) 2014 Mike Frysinger                                    *
 *   Copyright (C) 2014, 2015, 2016 Benno Schulenberg                     *
 *                                                                        *
8
9
10
11
 *   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.                               *
12
 *                                                                        *
13
14
15
16
 *   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.                 *
17
18
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
19
 *   along with this program.  If not, see http://www.gnu.org/licenses/.  *
20
21
22
 *                                                                        *
 **************************************************************************/

23
#include "proto.h"
24

25
26
#include <ctype.h>
#include <errno.h>
27
#include <glob.h>
28
#include <string.h>
Chris Allegretta's avatar
Chris Allegretta committed
29
#include <unistd.h>
30

31
#ifdef ENABLE_NANORC
32

33
34
35
36
#ifndef RCFILE_NAME
#define RCFILE_NAME ".nanorc"
#endif

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

125
static bool errors = FALSE;
126
	/* Whether we got any errors while parsing an rcfile. */
127
static size_t lineno = 0;
128
	/* If we did, the line number where the last error occurred. */
129
static char *nanorc = NULL;
130
	/* The path to the rcfile we're parsing. */
131
#ifndef DISABLE_COLOR
132
133
134
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. */
135
static syntaxtype *live_syntax;
136
	/* The syntax that is currently being parsed. */
137
static colortype *lastcolor = NULL;
138
139
	/* The end of the color list for the current syntax. */
#endif
140

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
141
142
143
/* 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
144
void rcfile_error(const char *msg, ...)
145
146
147
{
    va_list ap;

148
149
150
    if (ISSET(QUIET))
	return;

151
    fprintf(stderr, "\n");
152
153
    if (lineno > 0) {
	errors = TRUE;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
154
	fprintf(stderr, _("Error in %s on line %lu: "), nanorc, (unsigned long)lineno);
155
    }
Chris Allegretta's avatar
Chris Allegretta committed
156

157
    va_start(ap, msg);
158
    vfprintf(stderr, _(msg), ap);
159
    va_end(ap);
160
161

    fprintf(stderr, "\n");
162
}
163
#endif /* ENABLE_NANORC */
164

165
#if defined(ENABLE_NANORC) || !defined(DISABLE_HISTORIES)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
166
167
168
/* 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
169
char *parse_next_word(char *ptr)
170
{
171
    while (!isblank((unsigned char)*ptr) && *ptr != '\0')
172
173
174
	ptr++;

    if (*ptr == '\0')
175
	return ptr;
176

177
178
    /* Null-terminate and advance ptr. */
    *ptr++ = '\0';
179

180
    while (isblank((unsigned char)*ptr))
181
182
183
184
	ptr++;

    return ptr;
}
185
#endif /* ENABLE_NANORC || !DISABLE_HISTORIES */
186

187
#ifdef ENABLE_NANORC
188
189
190
191
/* 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
192
193
char *parse_argument(char *ptr)
{
194
    const char *ptr_save = ptr;
Chris Allegretta's avatar
Chris Allegretta committed
195
196
197
198
199
200
201
202
203
204
205
    char *last_quote = NULL;

    assert(ptr != NULL);

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

    do {
	ptr++;
	if (*ptr == '"')
	    last_quote = ptr;
206
    } while (*ptr != '\0');
Chris Allegretta's avatar
Chris Allegretta committed
207
208
209
210
211
212

    if (last_quote == NULL) {
	if (*ptr == '\0')
	    ptr = NULL;
	else
	    *ptr++ = '\0';
213
	rcfile_error(N_("Argument '%s' has an unterminated \""), ptr_save);
Chris Allegretta's avatar
Chris Allegretta committed
214
215
216
217
218
    } else {
	*last_quote = '\0';
	ptr = last_quote + 1;
    }
    if (ptr != NULL)
219
	while (isblank((unsigned char)*ptr))
Chris Allegretta's avatar
Chris Allegretta committed
220
221
222
223
	    ptr++;
    return ptr;
}

224
#ifndef DISABLE_COLOR
225
226
/* Pass over the current regex string in the line starting at ptr,
 * null-terminate it, and return a pointer to the /next/ word. */
227
228
char *parse_next_regex(char *ptr)
{
229
230
    assert(ptr != NULL);

231
232
233
    /* Continue until the end of line, or until a " followed by a
     * blank character or the end of line. */
    while (*ptr != '\0' && (*ptr != '"' ||
234
		(*(ptr + 1) != '\0' && !isblank((unsigned char)ptr[1]))))
235
236
	ptr++;

237
238
239
    assert(*ptr == '"' || *ptr == '\0');

    if (*ptr == '\0') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
240
241
	rcfile_error(
		N_("Regex strings must begin and end with a \" character"));
242
	return NULL;
243
    }
244

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
245
    /* Null-terminate and advance ptr. */
246
247
    *ptr++ = '\0';

248
    while (isblank((unsigned char)*ptr))
249
250
251
252
253
	ptr++;

    return ptr;
}

254
/* Compile the regular expression regex to see if it's valid.  Return
255
 * TRUE if it is, and FALSE otherwise. */
256
bool nregcomp(const char *regex, int compile_flags)
257
{
258
    regex_t preg;
259
    const char *r = fixbounds(regex);
260
    int rc = regcomp(&preg, r, compile_flags);
261
262

    if (rc != 0) {
263
	size_t len = regerror(rc, &preg, NULL, 0);
264
265
	char *str = charalloc(len);

266
	regerror(rc, &preg, str, len);
267
	rcfile_error(N_("Bad regex \"%s\": %s"), r, str);
268
269
	free(str);
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
270

271
    regfree(&preg);
272
    return (rc == 0);
273
274
}

275
276
/* 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. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
277
void parse_syntax(char *ptr)
278
{
279
280
    char *nameptr;
	/* A pointer to what should be the name of the syntax. */
281

282
283
    opensyntax = FALSE;

284
    assert(ptr != NULL);
285

286
287
288
    /* Check that the syntax name is not empty. */
    if (*ptr == '\0' || (*ptr == '"' &&
			(*(ptr + 1) == '\0' || *(ptr + 1) == '"'))) {
289
	rcfile_error(N_("Missing syntax name"));
290
	return;
291
    }
292

293
294
295
296
297
298
    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"));
299
	return;
300
    }
301

302
303
    /* Strip the end quote. */
    nameptr[strlen(nameptr) - 1] = '\0';
304

305
306
307
308
309
310
    /* Redefining the "none" syntax is not allowed. */
    if (strcmp(nameptr, "none") == 0) {
	rcfile_error(N_("The \"none\" syntax is reserved"));
	return;
    }

311
    /* Initialize a new syntax struct. */
312
313
314
315
316
317
318
    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;
    live_syntax->formatter = NULL;
319
320
321
#ifdef ENABLE_COMMENT
    live_syntax->comment = mallocstrcpy(NULL, GENERAL_COMMENT_CHARACTER);
#endif
322
    live_syntax->color = NULL;
323
    lastcolor = NULL;
324
    live_syntax->nmultis = 0;
325
326

    /* Hook the new syntax in at the top of the list. */
327
328
    live_syntax->next = syntaxes;
    syntaxes = live_syntax;
329

330
331
    opensyntax = TRUE;

332
#ifdef DEBUG
333
    fprintf(stderr, "Starting a new syntax type: \"%s\"\n", nameptr);
334
335
#endif

336
    /* The default syntax should have no associated extensions. */
337
    if (strcmp(live_syntax->name, "default") == 0 && *ptr != '\0') {
338
	rcfile_error(
339
		N_("The \"default\" syntax does not accept extensions"));
340
341
342
	return;
    }

343
344
    /* If there seem to be extension regexes, pick them up. */
    if (*ptr != '\0')
345
	grab_and_store("extension", ptr, &live_syntax->extensions);
346
}
347
#endif /* !DISABLE_COLOR */
348

349
350
351
/* Check whether the given executable function is "universal" (meaning
 * any horizontal movement or deletion) and thus is present in almost
 * all menus. */
352
bool is_universal(void (*func)(void))
353
354
{
    if (func == do_left || func == do_right ||
355
	func == do_home_void || func == do_end_void ||
356
#ifndef NANO_TINY
357
	func == do_prev_word_void || func == do_next_word_void ||
358
#endif
359
	func == do_delete || func == do_backspace ||
360
361
	func == do_cut_text_void || func == do_uncut_text ||
	func == do_tab || func == do_enter || func == do_verbatim_input)
362
363
364
365
366
	return TRUE;
    else
	return FALSE;
}

367
/* Bind or unbind a key combo, to or from a function. */
368
void parse_binding(char *ptr, bool dobind)
369
370
{
    char *keyptr = NULL, *keycopy = NULL, *funcptr = NULL, *menuptr = NULL;
371
    sc *s, *newsc = NULL;
372
    int menu;
373
374
375

    assert(ptr != NULL);

376
377
378
379
#ifdef DEBUG
    fprintf(stderr, "Starting the rebinding code...\n");
#endif

380
381
382
383
384
385
386
387
    if (*ptr == '\0') {
	rcfile_error(N_("Missing key name"));
	return;
    }

    keyptr = ptr;
    ptr = parse_next_word(ptr);
    keycopy = mallocstrcpy(NULL, keyptr);
388
389
390

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

    /* Uppercase only the first two or three characters of the key name. */
395
396
    keycopy[0] = toupper((unsigned char)keycopy[0]);
    keycopy[1] = toupper((unsigned char)keycopy[1]);
397
398
    if (keycopy[0] == 'M' && keycopy[1] == '-') {
	if (strlen(keycopy) > 2)
399
	    keycopy[2] = toupper((unsigned char)keycopy[2]);
400
401
	else {
	    rcfile_error(N_("Key name is too short"));
Benno Schulenberg's avatar
Benno Schulenberg committed
402
	    goto free_copy;
403
404
	}
    }
405

406
407
408
    /* 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"))
409
	keycopy[1] = tolower((unsigned char)keycopy[1]);
410
    else if (keycopy[0] != '^' && keycopy[0] != 'M' && keycopy[0] != 'F') {
411
	rcfile_error(N_("Key name must begin with \"^\", \"M\", or \"F\""));
Benno Schulenberg's avatar
Benno Schulenberg committed
412
	goto free_copy;
413
    } else if ((keycopy[0] == 'M' && keycopy[1] != '-') ||
414
415
		(keycopy[0] == '^' && ((keycopy[1] < 'A' || keycopy[1] > 'z') ||
		keycopy[1] == '[' || keycopy[1] == '`' ||
416
417
418
		(strlen(keycopy) > 2 && strcmp(keycopy, "^Space") != 0))) ||
		(strlen(keycopy) > 3 && strcmp(keycopy, "^Space") != 0 &&
		strcmp(keycopy, "M-Space") != 0)) {
419
420
	rcfile_error(N_("Key name %s is invalid"), keycopy);
	goto free_copy;
421
422
    }

423
424
425
    if (dobind) {
	funcptr = ptr;
	ptr = parse_next_word(ptr);
426

427
	if (funcptr[0] == '\0') {
428
	    rcfile_error(N_("Must specify a function to bind the key to"));
Benno Schulenberg's avatar
Benno Schulenberg committed
429
	    goto free_copy;
430
	}
431
432
433
434
435
    }

    menuptr = ptr;
    ptr = parse_next_word(ptr);

436
    if (menuptr[0] == '\0') {
437
	/* TRANSLATORS: Do not translate the word "all". */
438
	rcfile_error(N_("Must specify a menu (or \"all\") in which to bind/unbind the key"));
Benno Schulenberg's avatar
Benno Schulenberg committed
439
	goto free_copy;
440
    }
441
442

    if (dobind) {
443
	newsc = strtosc(funcptr);
444
	if (newsc == NULL) {
445
	    rcfile_error(N_("Cannot map name \"%s\" to a function"), funcptr);
Benno Schulenberg's avatar
Benno Schulenberg committed
446
	    goto free_copy;
447
	}
448
449
    }

450
451
452
    menu = strtomenu(menuptr);
    if (menu < 1) {
	rcfile_error(N_("Cannot map name \"%s\" to a menu"), menuptr);
Benno Schulenberg's avatar
Benno Schulenberg committed
453
	goto free_copy;
454
455
    }

456
#ifdef DEBUG
457
    if (dobind)
458
459
	fprintf(stderr, "newsc address is now %ld, assigned func = %ld, menu = %x\n",
	    (long)&newsc, (long)newsc->scfunc, menu);
460
    else
461
	fprintf(stderr, "unbinding \"%s\" from menu %x\n", keycopy, menu);
462
463
#endif

464
    if (dobind) {
465
466
467
468
469
470
471
472
	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;

473
#ifndef NANO_TINY
474
475
476
	/* Handle the special case of the toggles. */
	if (newsc->scfunc == do_toggle_void)
	    mask = MMAIN;
477
#endif
478

479
480
481
482
483
484
485
486
487
	/* 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
488
	    goto free_copy;
489
490
	}

491
	newsc->menus = menu;
492
	assign_keyinfo(newsc, keycopy, 0);
493

494
	/* Do not allow rebinding a frequent escape-sequence starter: Esc [. */
495
	if (newsc->meta && newsc->keycode == 91) {
496
497
	    rcfile_error(N_("Sorry, keystroke \"%s\" may not be rebound"), newsc->keystr);
	    free(newsc);
Benno Schulenberg's avatar
Benno Schulenberg committed
498
	    goto free_copy;
499
	}
500
501
#ifdef DEBUG
	fprintf(stderr, "s->keystr = \"%s\"\n", newsc->keystr);
502
	fprintf(stderr, "s->keycode = \"%d\"\n", newsc->keycode);
503
#endif
504
    }
505

506
    /* Now find and delete any existing same shortcut in the menu(s). */
507
    for (s = sclist; s != NULL; s = s->next) {
508
	if ((s->menus & menu) && !strcmp(s->keystr, keycopy)) {
509
#ifdef DEBUG
510
	    fprintf(stderr, "deleting entry from among menus %x\n", s->menus);
511
#endif
512
	    s->menus &= ~menu;
513
514
	}
    }
515
516

    if (dobind) {
517
#ifndef NANO_TINY
518
519
520
	/* If this is a toggle, copy its sequence number. */
	if (newsc->scfunc == do_toggle_void) {
	    for (s = sclist; s != NULL; s = s->next)
521
		if (s->scfunc == do_toggle_void && s->toggle == newsc->toggle)
522
523
524
		    newsc->ordinal = s->ordinal;
	} else
	    newsc->ordinal = 0;
525
#endif
526
527
528
	/* Add the new shortcut at the start of the list. */
	newsc->next = sclist;
	sclist = newsc;
Benno Schulenberg's avatar
Benno Schulenberg committed
529
530
531
532
533
	return;
    }

  free_copy:
    free(keycopy);
534
535
}

536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
/* Verify that the given file is not a folder nor a device. */
bool is_good_file(char *file)
{
    struct stat rcinfo;

    /* If the thing exists, it may not be a directory nor a device. */
    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;
}

551
#ifndef DISABLE_COLOR
552
553
/* Read and parse one included syntax file. */
static void parse_one_include(char *file)
554
555
556
557
{
    FILE *rcstream;

    /* Don't open directories, character files, or block files. */
558
    if (!is_good_file(file))
559
	return;
560

561
562
563
564
565
    /* Open the included syntax file. */
    rcstream = fopen(file, "rb");

    if (rcstream == NULL) {
	rcfile_error(_("Error reading %s: %s"), file, strerror(errno));
566
	return;
567
568
    }

569
    /* Use the name and line number position of the included syntax file
570
     * while parsing it, so we can know where any errors in it are. */
571
    nanorc = file;
572
573
    lineno = 0;

574
#ifdef DEBUG
575
    fprintf(stderr, "Parsing file \"%s\"\n", file);
576
577
#endif

578
    parse_rcfile(rcstream, TRUE);
579
580
}

581
582
/* Expand globs in the passed name, and parse the resultant files. */
void parse_includes(char *ptr)
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
{
    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)
598
599
	    parse_one_include(files.gl_pathv[i]);
    } else
600
	rcfile_error(_("Error expanding %s: %s"), option, strerror(errno));
601

602
603
604
    globfree(&files);
    free(expanded);

605
    /* We're done with the included file(s).  Restore the original
606
607
608
609
610
     * filename and line number position. */
    nanorc = nanorc_save;
    lineno = lineno_save;
}

611
612
/* Return the short value corresponding to the color named in colorname,
 * and set bright to TRUE if that color is bright. */
613
short color_to_short(const char *colorname, bool *bright)
614
615
616
617
618
619
620
{
    if (strncasecmp(colorname, "bright", 6) == 0) {
	*bright = TRUE;
	colorname += 6;
    }

    if (strcasecmp(colorname, "green") == 0)
Benno Schulenberg's avatar
Benno Schulenberg committed
621
	return COLOR_GREEN;
622
    else if (strcasecmp(colorname, "red") == 0)
Benno Schulenberg's avatar
Benno Schulenberg committed
623
	return COLOR_RED;
624
    else if (strcasecmp(colorname, "blue") == 0)
Benno Schulenberg's avatar
Benno Schulenberg committed
625
	return COLOR_BLUE;
626
    else if (strcasecmp(colorname, "white") == 0)
Benno Schulenberg's avatar
Benno Schulenberg committed
627
	return COLOR_WHITE;
628
    else if (strcasecmp(colorname, "yellow") == 0)
Benno Schulenberg's avatar
Benno Schulenberg committed
629
	return COLOR_YELLOW;
630
    else if (strcasecmp(colorname, "cyan") == 0)
Benno Schulenberg's avatar
Benno Schulenberg committed
631
	return COLOR_CYAN;
632
    else if (strcasecmp(colorname, "magenta") == 0)
Benno Schulenberg's avatar
Benno Schulenberg committed
633
	return COLOR_MAGENTA;
634
    else if (strcasecmp(colorname, "black") == 0)
Benno Schulenberg's avatar
Benno Schulenberg committed
635
636
637
	return COLOR_BLACK;

    rcfile_error(N_("Color \"%s\" not understood.\n"
638
639
640
641
		"Valid colors are \"green\", \"red\", \"blue\",\n"
		"\"white\", \"yellow\", \"cyan\", \"magenta\" and\n"
		"\"black\", with the optional prefix \"bright\"\n"
		"for foreground colors."), colorname);
Benno Schulenberg's avatar
Benno Schulenberg committed
642
    return -1;
643
644
}

645
/* Parse the color string in the line at ptr, and add it to the current
646
647
648
 * 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)
649
{
650
    short fg, bg;
651
    bool bright = FALSE;
652
    char *item;
653

654
    if (!opensyntax) {
655
	rcfile_error(
656
657
		N_("A '%s' command requires a preceding 'syntax' command"),
		"color");
658
659
660
	return;
    }

661
    if (*ptr == '\0') {
662
	rcfile_error(N_("Missing color name"));
663
	return;
664
665
    }

666
    item = ptr;
667
    ptr = parse_next_word(ptr);
668
    if (!parse_color_names(item, &fg, &bg, &bright))
669
	return;
670

671
    if (*ptr == '\0') {
672
	rcfile_error(N_("Missing regex string after '%s' command"), "color");
673
674
675
	return;
    }

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
676
    /* Now for the fun part.  Start adding regexes to individual strings
677
678
     * in the colorstrings array, woo! */
    while (ptr != NULL && *ptr != '\0') {
679
	colortype *newcolor = NULL;
680
	    /* The container for a color plus its regexes. */
Benno Schulenberg's avatar
Benno Schulenberg committed
681
682
	bool goodstart;
	    /* Whether the start expression was valid. */
683
	bool expectend = FALSE;
Benno Schulenberg's avatar
Benno Schulenberg committed
684
	    /* Whether to expect an end= line. */
685

686
	if (strncasecmp(ptr, "start=", 6) == 0) {
687
	    ptr += 6;
688
	    expectend = TRUE;
689
690
691
	}

	if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
692
693
	    rcfile_error(
		N_("Regex strings must begin and end with a \" character"));
694
	    ptr = parse_next_regex(ptr);
695
696
	    continue;
	}
697

698
	item = ++ptr;
699
	ptr = parse_next_regex(ptr);
700
701
702
	if (ptr == NULL)
	    break;

703
	if (*item == '\0') {
704
705
706
	    rcfile_error(N_("Empty regex string"));
	    goodstart = FALSE;
	} else
707
	    goodstart = nregcomp(item, rex_flags);
Benno Schulenberg's avatar
Benno Schulenberg committed
708
709
710
711

	/* If the starting regex is valid, initialize a new color struct,
	 * and hook it in at the tail of the linked list. */
	if (goodstart) {
712
713
	    newcolor = (colortype *)nmalloc(sizeof(colortype));

714
715
716
	    newcolor->fg = fg;
	    newcolor->bg = bg;
	    newcolor->bright = bright;
717
	    newcolor->rex_flags = rex_flags;
718

719
	    newcolor->start_regex = mallocstrcpy(NULL, item);
720
721
	    newcolor->start = NULL;

722
	    newcolor->end_regex = NULL;
723
	    newcolor->end = NULL;
724

725
	    newcolor->next = NULL;
726

727
	    if (lastcolor == NULL)
Benno Schulenberg's avatar
Benno Schulenberg committed
728
		live_syntax->color = newcolor;
729
	    else
730
		lastcolor->next = newcolor;
731

732
	    lastcolor = newcolor;
Benno Schulenberg's avatar
Benno Schulenberg committed
733
	}
Chris Allegretta's avatar
Chris Allegretta committed
734

735
736
	if (!expectend)
	    continue;
737

738
739
740
741
	if (ptr == NULL || strncasecmp(ptr, "end=", 4) != 0) {
	    rcfile_error(N_("\"start=\" requires a corresponding \"end=\""));
	    return;
	}
742

743
744
745
746
747
	ptr += 4;
	if (*ptr != '"') {
	    rcfile_error(N_("Regex strings must begin and end with a \" character"));
	    continue;
	}
748

749
	item = ++ptr;
750
751
	ptr = parse_next_regex(ptr);
	if (ptr == NULL)
752
	    break;
753

754
	if (*item == '\0') {
755
756
757
758
	    rcfile_error(N_("Empty regex string"));
	    continue;
	}

759
760
	/* If the start regex was invalid, skip past the end regex
	 * to stay in sync. */
Benno Schulenberg's avatar
Benno Schulenberg committed
761
	if (!goodstart)
762
763
764
	    continue;

	/* If it's valid, save the ending regex string. */
765
766
	if (nregcomp(item, rex_flags))
	    newcolor->end_regex = mallocstrcpy(NULL, item);
767
768
769
770

	/* Lame way to skip another static counter. */
	newcolor->id = live_syntax->nmultis;
	live_syntax->nmultis++;
771
    }
772
}
773

774
775
776
/* Parse the color name, or pair of color names, in combostr. */
bool parse_color_names(char *combostr, short *fg, short *bg, bool *bright)
{
777
    char *comma = strchr(combostr, ',');
778

779
    if (comma != NULL) {
780
781
	*bg = color_to_short(comma + 1, bright);
	if (*bright) {
782
	    rcfile_error(N_("A background color cannot be bright"));
783
	    return FALSE;
784
	}
785
	*comma = '\0';
786
787
788
    } else
	*bg = -1;

789
    if (comma != combostr) {
790
791
	*fg = color_to_short(combostr, bright);

792
	/* If the specified foreground color is bad, ignore the regexes. */
793
	if (*fg == -1)
794
	    return FALSE;
795
796
797
    } else
	*fg = -1;

798
    return TRUE;
799
800
}

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

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

813
814
    /* The default syntax doesn't take any file matching stuff. */
    if (strcmp(live_syntax->name, "default") == 0 && *ptr != '\0') {
815
	rcfile_error(
816
		N_("The \"default\" syntax does not accept '%s' regexes"), kind);
817
818
819
820
	return;
    }

    if (*ptr == '\0') {
821
	rcfile_error(N_("Missing regex string after '%s' command"), kind);
822
823
824
	return;
    }

825
826
827
828
829
830
    lastthing = *storage;

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

831
    /* Now gather any valid regexes and add them to the linked list. */
832
833
    while (*ptr != '\0') {
	const char *regexstring;
834
	regexlisttype *newthing;
835
836
837
838

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

842
	regexstring = ++ptr;
843
844
	ptr = parse_next_regex(ptr);
	if (ptr == NULL)
845
	    return;
846

847
	/* If the regex string is malformed, skip it. */
848
	if (!nregcomp(regexstring, NANO_REG_EXTENDED | REG_NOSUB))
849
	    continue;
850

851
852
853
854
	/* 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;
855

856
857
858
859
	if (lastthing == NULL)
	    *storage = newthing;
	else
	    lastthing->next = newthing;
860

861
	lastthing = newthing;
862
863
    }
}
864

865
/* Gather and store the string after a comment/linter/formatter command. */
866
void pick_up_name(const char *kind, char *ptr, char **storage)
867
868
869
{
    assert(ptr != NULL);

870
    if (!opensyntax) {
871
	rcfile_error(
872
		N_("A '%s' command requires a preceding 'syntax' command"), kind);
873
874
875
876
	return;
    }

    if (*ptr == '\0') {
877
	rcfile_error(N_("Missing argument after '%s'"), kind);
878
879
880
	return;
    }

881
882
883
    /* If the argument starts with a quote, find the terminating quote. */
    if (*ptr == '"') {
	char *look = ++ptr;
884

885
	look += strlen(ptr);
886

887
	while (*look != '"') {
888
	    if (--look < ptr) {
889
890
891
892
		rcfile_error(N_("Argument of '%s' lacks closing \""), kind);
		return;
	    }
	}
893
	*look = '\0';
894
    }
895

896
    *storage = mallocstrcpy(*storage, ptr);
897

898
}
899
#endif /* !DISABLE_COLOR */
900

901
902
/* Verify that the user has not unmapped every shortcut for a
 * function that we consider 'vital' (such as "Exit"). */
903
904
905
906
907
static void check_vitals_mapped(void)
{
    subnfunc *f;
    int v;
#define VITALS 5
908
    void (*vitals[VITALS])(void) = { do_exit, do_exit, do_cancel, do_cancel, do_cancel };
909
910
911
    int inmenus[VITALS] = { MMAIN, MHELP, MWHEREIS, MREPLACE, MGOTOLINE };

    for  (v = 0; v < VITALS; v++) {
912
913
914
915
916
	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 "
917
					"\"%s\".  Exiting.\n"), f->desc);
918
		    fprintf(stderr, _("If needed, use nano with the -I option "
919
920
					"to adjust your nanorc settings.\n"));
		    exit(1);
921
922
923
924
		}
		break;
	    }
	}
925
926
927
    }
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
928
/* Parse the rcfile, once it has been opened successfully at rcstream,
929
930
931
 * 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)
932
{
933
934
    char *buf = NULL;
    ssize_t len;
935
    size_t n = 0;
936
937
938

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
942
	/* Ignore the newline. */
943
944
	if (buf[len - 1] == '\n')
	    buf[len - 1] = '\0';
945
946
947

	lineno++;
	ptr = buf;
948
	while (isblank((unsigned char)*ptr))
949
950
	    ptr++;

951
952
953
	/* If we have a blank line or a comment, skip to the next
	 * line. */
	if (*ptr == '\0' || *ptr == '#')
954
955
	    continue;

956
	/* Otherwise, skip to the next space. */
957
958
959
	keyword = ptr;
	ptr = parse_next_word(ptr);

960
#ifndef DISABLE_COLOR
961
962
	/* Handle extending first... */
	if (strcasecmp(keyword, "extendsyntax") == 0) {
963
	    syntaxtype *sint;
964
965
966
	    char *syntaxname = ptr;

	    ptr = parse_next_word(ptr);
967
968
969

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

972
973
974
	    if (sint == NULL) {
		rcfile_error(N_("Could not find syntax \"%s\" to extend"),
				syntaxname);
975
		opensyntax = FALSE;
976
977
		continue;
	    }
978
979
980
981

	    live_syntax = sint;
	    opensyntax = TRUE;

982
	    /* Refind the tail of the color list for this syntax. */
983
984
985
986
	    lastcolor = sint->color;
	    if (lastcolor != NULL)
		while (lastcolor->next != NULL)
		    lastcolor = lastcolor->next;
987

988
989
	    keyword = ptr;
	    ptr = parse_next_word(ptr);
990
991
	}

992
	/* Try to parse the keyword. */
993
	if (strcasecmp(keyword, "syntax") == 0) {
994
	    if (opensyntax && lastcolor == NULL)
995
		rcfile_error(N_("Syntax \"%s\" has no color commands"),
996
				live_syntax->name);
Chris Allegretta's avatar
Chris Allegretta committed
997
	    parse_syntax(ptr);
998
	}
999
1000
	else if (strcasecmp(keyword, "header") == 0)
	    grab_and_store("header", ptr, &live_syntax->headers);
1001
	else if (strcasecmp(keyword, "magic") == 0)
1002
#ifdef HAVE_LIBMAGIC
1003
	    grab_and_store("magic", ptr, &live_syntax->magics);
1004
1005
#else
	    ;
1006
1007
1008
1009
1010
1011
#endif
	else if (strcasecmp(keyword, "comment") == 0)
#ifdef ENABLE_COMMENT
	    pick_up_name("comment", ptr, &live_syntax->comment);
#else
	    ;
1012
#endif
1013
	else if (strcasecmp(keyword, "color") == 0)
1014
	    parse_colors(ptr, NANO_REG_EXTENDED);
1015
	else if (strcasecmp(keyword, "icolor") == 0)
1016
	    parse_colors(ptr, NANO_REG_EXTENDED | REG_ICASE);
1017
	else if (strcasecmp(keyword, "linter") == 0)
1018
	    pick_up_name("linter", ptr, &live_syntax->linter);
1019
	else if (strcasecmp(keyword, "formatter") == 0)
1020
#ifndef DISABLE_SPELLER
1021
	    pick_up_name("formatter", ptr, &live_syntax->formatter);
1022
1023
1024
#else
	    ;
#endif
1025
1026
1027
1028
1029
1030
	else if (syntax_only)
	    rcfile_error(N_("Command \"%s\" not allowed in included file"),
					keyword);
	else if (strcasecmp(keyword, "include") == 0)
	    parse_includes(ptr);
	else
1031
#endif /* !DISABLE_COLOR */
1032
1033
1034
1035
	if (strcasecmp(keyword, "set") == 0)
	    set = 1;
	else if (strcasecmp(keyword, "unset") == 0)
	    set = -1;
1036
	else if (strcasecmp(keyword, "bind") == 0)
1037
	    parse_binding(ptr, TRUE);
1038
	else if (strcasecmp(keyword, "unbind") == 0)
1039
	    parse_binding(ptr, FALSE);
1040
	else
1041
	    rcfile_error(N_("Command \"%s\" not understood"), keyword);
1042

1043
#ifndef DISABLE_COLOR
1044
1045
	/* If a syntax was extended, it stops at the end of the command. */
	if (live_syntax != syntaxes)
1046
	    opensyntax = FALSE;
1047
1048
#endif

1049
1050
1051
1052
	if (set == 0)
	    continue;

	if (*ptr == '\0') {
1053
	    rcfile_error(N_("Missing option"));
1054
1055
1056
1057
1058
1059
	    continue;
	}

	option = ptr;
	ptr = parse_next_word(ptr);

1060
	/* Find the just read name among the existing options. */
1061
	for (i = 0; rcopts[i].name != NULL; i++) {
1062
1063
1064
1065
1066
1067
1068
1069
1070
	    if (strcasecmp(option, rcopts[i].name) == 0)
		break;
	}

	if (rcopts[i].name == NULL) {
	    rcfile_error(N_("Unknown option \"%s\""), option);
	    continue;
	}

1071
#ifdef DEBUG
1072
1073
	fprintf(stderr, "    Option name = \"%s\"\n", rcopts[i].name);
	fprintf(stderr, "    Flag = %ld\n", rcopts[i].flag);
1074
#endif
1075
1076
	/* First handle unsetting. */
	if (set == -1) {
1077
	    if (rcopts[i].flag != 0)
1078
		UNSET(rcopts[i].flag);
1079
	    else
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
		rcfile_error(N_("Cannot unset option \"%s\""), rcopts[i].name);
	    continue;
	}

	/* If the option has a flag, it doesn't take an argument. */
	if (rcopts[i].flag != 0) {
	    SET(rcopts[i].flag);
	    continue;
	}

	/* The option doesn't have a flag, so it takes an argument. */
	if (*ptr == '\0') {
	    rcfile_error(N_("Option \"%s\" requires an argument"),
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1093
				rcopts[i].name);
1094
1095
1096
1097
1098
1099
1100
1101
1102
	    continue;
	}

	option = ptr;
	if (*option == '"')
	    option++;
	ptr = parse_argument(ptr);

	option = mallocstrcpy(NULL, option);
Chris Allegretta's avatar
Chris Allegretta committed
1103
#ifdef DEBUG
1104
	fprintf(stderr, "    Option argument = \"%s\"\n", option);
Chris Allegretta's avatar
Chris Allegretta committed
1105
#endif
1106
1107
	/* Make sure the option argument is a valid multibyte string. */
	if (!is_valid_mbstring(option)) {
1108
	    rcfile_error(N_("Argument is not a valid multibyte string"));
1109
1110
	    continue;
	}
1111

1112
#ifndef DISABLE_COLOR
1113
1114
	if (strcasecmp(rcopts[i].name, "titlecolor") == 0)
	    specified_color_combo[TITLE_BAR] = option;
1115
1116
	else if (strcasecmp(rcopts[i].name, "numbercolor") == 0)
	    specified_color_combo[LINE_NUMBER] = option;
1117
1118
	else if (strcasecmp(rcopts[i].name, "selectedcolor") == 0)
	    specified_color_combo[SELECTED_TEXT] = option;
1119
1120
1121
1122
1123
1124
1125
	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
1126
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1127
#ifndef DISABLE_OPERATINGDIR
1128
1129
1130
	if (strcasecmp(rcopts[i].name, "operatingdir") == 0)
	    operating_dir = option;
	else
Chris Allegretta's avatar
Chris Allegretta committed
1131
#endif
1132
#ifndef DISABLE_WRAPJUSTIFY
1133
1134
1135
1136
1137
	if (strcasecmp(rcopts[i].name, "fill") == 0) {
	    if (!parse_num(option, &wrap_at)) {
		rcfile_error(N_("Requested fill size \"%s\" is invalid"),
				option);
		wrap_at = -CHARS_FROM_EOL;
1138
	    } else
1139
		UNSET(NO_WRAP);
1140
	    free(option);
1141
	} else
Chris Allegretta's avatar
Chris Allegretta committed
1142
#endif
1143
#ifndef NANO_TINY
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
	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) {
	    whitespace = option;
	    if (mbstrlen(whitespace) != 2 || strlenpt(whitespace) != 2) {
		rcfile_error(N_("Two single-column characters required"));
		free(whitespace);
		whitespace = NULL;
	    } else {
		whitespace_len[0] = parse_mbchar(whitespace, NULL, NULL);
		whitespace_len[1] = parse_mbchar(whitespace +
1160
					whitespace_len[0], NULL, NULL);
1161
1162
	    }
	} else
1163
#endif
1164
#ifndef DISABLE_JUSTIFY
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
	if (strcasecmp(rcopts[i].name, "punct") == 0) {
	    punct = option;
	    if (has_blank_mbchars(punct)) {
		rcfile_error(N_("Non-blank characters required"));
		free(punct);
		punct = NULL;
	    }
	} else if (strcasecmp(rcopts[i].name, "brackets") == 0) {
	    brackets = option;
	    if (has_blank_mbchars(brackets)) {
		rcfile_error(N_("Non-blank characters required"));
		free(brackets);
		brackets = NULL;
	    }
	} else if (strcasecmp(rcopts[i].name, "quotestr") == 0)
	    quotestr = option;
	else
1182
#endif
1183
#ifndef NANO_TINY
1184
1185
1186
	if (strcasecmp(rcopts[i].name, "backupdir") == 0)
	    backup_dir = option;
	else
1187
1188
1189
	if (strcasecmp(rcopts[i].name, "wordchars") == 0)
	    word_chars = option;
	else
1190
#endif
1191
#ifndef DISABLE_SPELLER
1192
1193
1194
	if (strcasecmp(rcopts[i].name, "speller") == 0)
	    alt_speller = option;
	else
1195
#endif
1196
1197
1198
1199
1200
	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;
1201
1202
	    }
	    free(option);
1203
1204
	} else
	    assert(FALSE);
1205
    }
1206

1207
#ifndef DISABLE_COLOR
1208
    if (opensyntax && lastcolor == NULL)
1209
	rcfile_error(N_("Syntax \"%s\" has no color commands"),
1210
			live_syntax->name);
1211

1212
    opensyntax = FALSE;
1213
#endif
1214

Chris Allegretta's avatar
Chris Allegretta committed
1215
    free(buf);
1216
1217
    fclose(rcstream);
    lineno = 0;
1218

1219
1220
1221
    return;
}

1222
1223
/* Read and interpret one of the two nanorc files. */
void parse_one_nanorc(void)
1224
1225
1226
{
    FILE *rcstream;

1227
    /* Don't try to open directories nor devices. */
1228
    if (!is_good_file(nanorc))
1229
	return;
1230

1231
#ifdef DEBUG
1232
    fprintf(stderr, "Going to parse file \"%s\"\n", nanorc);
1233
1234
#endif

1235
    rcstream = fopen(nanorc, "rb");
1236
1237
1238

    /* If opening the file succeeded, parse it.  Otherwise, only
     * complain if the file actually exists. */
1239
    if (rcstream != NULL)
1240
	parse_rcfile(rcstream, FALSE);
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
    else if (errno != ENOENT)
	rcfile_error(N_("Error reading %s: %s"), nanorc, strerror(errno));
}

/* First read the system-wide rcfile, then the user's rcfile. */
void do_rcfiles(void)
{
    nanorc = mallocstrcpy(nanorc, SYSCONFDIR "/nanorc");

    /* Process the system-wide nanorc. */
    parse_one_nanorc();
1252

1253
1254
    /* When configured with --disable-wrapping-as-root, turn wrapping off
     * for root, so that only root's .nanorc or --fill can turn it on. */
1255
#ifdef DISABLE_ROOTWRAPPING
1256
1257
1258
    if (geteuid() == NANO_ROOT_UID)
	SET(NO_WRAP);
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1259

1260
    get_homedir();
1261

1262
    if (homedir == NULL)
1263
	rcfile_error(N_("I can't find my home directory!  Wah!"));
1264
    else {
1265
1266
	nanorc = charealloc(nanorc, strlen(homedir) + strlen(RCFILE_NAME) + 2);
	sprintf(nanorc, "%s/%s", homedir, RCFILE_NAME);
1267

1268
1269
	/* Process the current user's nanorc. */
	parse_one_nanorc();
1270
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1271

1272
1273
    check_vitals_mapped();

1274
    free(nanorc);
1275

1276
    if (errors && !ISSET(QUIET) && !ISSET(NO_PAUSES)) {
1277
	errors = FALSE;
1278
	fprintf(stderr, _("\nPress Enter to continue starting nano.\n"));
1279
1280
1281
	while (getchar() != '\n')
	    ;
    }
1282
1283
}

1284
#endif /* ENABLE_NANORC */