rcfile.c 33.9 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
static syntaxtype *live_syntax;
127
	/* The syntax that is currently being parsed. */
128
129
130
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
217
/* Pass over the current regex string in the line starting at ptr,
 * null-terminate it, and return a pointer to the /next/ word. */
218
219
char *parse_next_regex(char *ptr)
{
220
221
    assert(ptr != NULL);

222
223
224
225
    /* 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(*(ptr + 1)))))
226
227
	ptr++;

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

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

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

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

    return ptr;
}

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

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

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

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

266
267
/* 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
268
void parse_syntax(char *ptr)
269
{
270
271
    char *nameptr;
	/* A pointer to what should be the name of the syntax. */
272

273
274
    opensyntax = FALSE;

275
    assert(ptr != NULL);
276

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

284
285
286
287
288
289
    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"));
290
	return;
291
    }
292

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

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

302
    /* Initialize a new syntax struct. */
303
304
305
306
307
308
309
310
    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;
    live_syntax->color = NULL;
311
    endcolor = NULL;
312
    live_syntax->nmultis = 0;
313
314

    /* Hook the new syntax in at the top of the list. */
315
316
    live_syntax->next = syntaxes;
    syntaxes = live_syntax;
317

318
319
    opensyntax = TRUE;

320
#ifdef DEBUG
321
    fprintf(stderr, "Starting a new syntax type: \"%s\"\n", nameptr);
322
323
#endif

324
    /* The default syntax should have no associated extensions. */
325
    if (strcmp(live_syntax->name, "default") == 0 && *ptr != '\0') {
326
	rcfile_error(
327
		N_("The \"default\" syntax does not accept extensions"));
328
329
330
	return;
    }

331
332
    /* If there seem to be extension regexes, pick them up. */
    if (*ptr != '\0')
333
	grab_and_store("extension", ptr, &live_syntax->extensions);
334
}
335
#endif /* !DISABLE_COLOR */
336

337
338
339
340
341
342
343
/* 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 ||
344
#ifndef NANO_TINY
345
	func == do_prev_word_void || func == do_next_word_void ||
346
#endif
347
348
349
350
351
352
353
354
	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;
}

355
/* Bind or unbind a key combo, to or from a function. */
356
void parse_binding(char *ptr, bool dobind)
357
358
{
    char *keyptr = NULL, *keycopy = NULL, *funcptr = NULL, *menuptr = NULL;
359
    sc *s, *newsc = NULL;
360
    int menu;
361
362
363

    assert(ptr != NULL);

364
365
366
367
#ifdef DEBUG
    fprintf(stderr, "Starting the rebinding code...\n");
#endif

368
369
370
371
372
373
374
375
    if (*ptr == '\0') {
	rcfile_error(N_("Missing key name"));
	return;
    }

    keyptr = ptr;
    ptr = parse_next_word(ptr);
    keycopy = mallocstrcpy(NULL, keyptr);
376
377
378

    if (strlen(keycopy) < 2) {
	rcfile_error(N_("Key name is too short"));
Benno Schulenberg's avatar
Benno Schulenberg committed
379
	goto free_copy;
380
381
382
383
384
385
386
387
388
389
    }

    /* 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
390
	    goto free_copy;
391
392
	}
    }
393

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

406
407
408
    if (dobind) {
	funcptr = ptr;
	ptr = parse_next_word(ptr);
409

410
	if (funcptr[0] == '\0') {
411
	    rcfile_error(N_("Must specify a function to bind the key to"));
Benno Schulenberg's avatar
Benno Schulenberg committed
412
	    goto free_copy;
413
	}
414
415
416
417
418
    }

    menuptr = ptr;
    ptr = parse_next_word(ptr);

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

    if (dobind) {
426
	newsc = strtosc(funcptr);
427
	if (newsc == NULL) {
428
	    rcfile_error(N_("Cannot map name \"%s\" to a function"), funcptr);
Benno Schulenberg's avatar
Benno Schulenberg committed
429
	    goto free_copy;
430
	}
431
432
    }

433
434
435
    menu = strtomenu(menuptr);
    if (menu < 1) {
	rcfile_error(N_("Cannot map name \"%s\" to a menu"), menuptr);
Benno Schulenberg's avatar
Benno Schulenberg committed
436
	goto free_copy;
437
438
    }

439
#ifdef DEBUG
440
    if (dobind)
441
442
	fprintf(stderr, "newsc address is now %ld, assigned func = %ld, menu = %x\n",
	    (long)&newsc, (long)newsc->scfunc, menu);
443
    else
444
	fprintf(stderr, "unbinding \"%s\" from menu %x\n", keycopy, menu);
445
446
#endif

447
    if (dobind) {
448
449
450
451
452
453
454
455
	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;

456
457
458
459
	/* Handle the special case of the toggles. */
	if (newsc->scfunc == do_toggle_void)
	    mask = MMAIN;

460
461
462
463
464
465
466
467
468
	/* 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
469
	    goto free_copy;
470
471
	}

472
	newsc->keystr = keycopy;
473
	newsc->menus = menu;
474
475
	newsc->type = strtokeytype(newsc->keystr);
	assign_keyinfo(newsc);
476

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

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

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

  free_copy:
    free(keycopy);
515
516
}

517

518
#ifndef DISABLE_COLOR
519
/* Read and parse additional syntax files. */
520
static void _parse_include(char *file)
521
522
523
524
{
    struct stat rcinfo;
    FILE *rcstream;

525
526
527
    /* 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). */
528
529

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

    /* Open the new syntax file. */
540
541
    if ((rcstream = fopen(file, "rb")) == NULL) {
	rcfile_error(_("Error reading %s: %s"), file,
542
		strerror(errno));
543
	return;
544
545
546
547
    }

    /* 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. */
548
    nanorc = file;
549
550
    lineno = 0;

551
#ifdef DEBUG
552
    fprintf(stderr, "Parsing file \"%s\"\n", file);
553
554
#endif

555
    parse_rcfile(rcstream, TRUE);
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
}

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

580
581
582
    globfree(&files);
    free(expanded);

583
584
585
586
587
588
    /* We're done with the new syntax file.  Restore the original
     * filename and line number position. */
    nanorc = nanorc_save;
    lineno = lineno_save;
}

589
590
/* Return the short value corresponding to the color named in colorname,
 * and set bright to TRUE if that color is bright. */
591
short color_to_short(const char *colorname, bool *bright)
592
593
594
595
596
597
598
599
600
{
    assert(colorname != NULL && bright != NULL);

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

    if (strcasecmp(colorname, "green") == 0)
Benno Schulenberg's avatar
Benno Schulenberg committed
601
	return COLOR_GREEN;
602
    else if (strcasecmp(colorname, "red") == 0)
Benno Schulenberg's avatar
Benno Schulenberg committed
603
	return COLOR_RED;
604
    else if (strcasecmp(colorname, "blue") == 0)
Benno Schulenberg's avatar
Benno Schulenberg committed
605
	return COLOR_BLUE;
606
    else if (strcasecmp(colorname, "white") == 0)
Benno Schulenberg's avatar
Benno Schulenberg committed
607
	return COLOR_WHITE;
608
    else if (strcasecmp(colorname, "yellow") == 0)
Benno Schulenberg's avatar
Benno Schulenberg committed
609
	return COLOR_YELLOW;
610
    else if (strcasecmp(colorname, "cyan") == 0)
Benno Schulenberg's avatar
Benno Schulenberg committed
611
	return COLOR_CYAN;
612
    else if (strcasecmp(colorname, "magenta") == 0)
Benno Schulenberg's avatar
Benno Schulenberg committed
613
	return COLOR_MAGENTA;
614
    else if (strcasecmp(colorname, "black") == 0)
Benno Schulenberg's avatar
Benno Schulenberg committed
615
616
617
	return COLOR_BLACK;

    rcfile_error(N_("Color \"%s\" not understood.\n"
618
619
620
621
		"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
622
    return -1;
623
624
}

625
626
627
/* 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. */
628
void parse_colors(char *ptr, bool icase)
629
{
630
    short fg, bg;
631
    bool bright = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
632
    char *fgstr;
633

634
    assert(ptr != NULL);
635

636
    if (!opensyntax) {
637
	rcfile_error(
638
		N_("Cannot add a color command without a syntax command"));
639
640
641
	return;
    }

642
    if (*ptr == '\0') {
643
	rcfile_error(N_("Missing color name"));
644
	return;
645
646
    }

647
648
    fgstr = ptr;
    ptr = parse_next_word(ptr);
649
650
    if (!parse_color_names(fgstr, &fg, &bg, &bright))
	return;
651

652
    if (*ptr == '\0') {
653
	rcfile_error(N_("Missing regex string"));
654
655
656
	return;
    }

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
657
    /* Now for the fun part.  Start adding regexes to individual strings
658
659
     * in the colorstrings array, woo! */
    while (ptr != NULL && *ptr != '\0') {
660
	colortype *newcolor;
661
	    /* The container for a color plus its regexes. */
662
	bool cancelled = FALSE;
663
	    /* The start expression was bad. */
664
665
	bool expectend = FALSE;
	    /* Do we expect an end= line? */
666

667
	if (strncasecmp(ptr, "start=", 6) == 0) {
668
	    ptr += 6;
669
	    expectend = TRUE;
670
671
672
	}

	if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
673
674
	    rcfile_error(
		N_("Regex strings must begin and end with a \" character"));
675
	    ptr = parse_next_regex(ptr);
676
677
	    continue;
	}
678

679
	fgstr = ++ptr;
680
	ptr = parse_next_regex(ptr);
681
682
683
	if (ptr == NULL)
	    break;

684
685
686
	/* Save the starting regex string if it's valid, and set up the
	 * color information. */
	if (nregcomp(fgstr, icase ? REG_ICASE : 0)) {
687
688
	    newcolor = (colortype *)nmalloc(sizeof(colortype));

689
690
691
	    newcolor->fg = fg;
	    newcolor->bg = bg;
	    newcolor->bright = bright;
692
	    newcolor->icase = icase;
693
694
695
696

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

697
	    newcolor->end_regex = NULL;
698
	    newcolor->end = NULL;
699

700
	    newcolor->next = NULL;
701

702
	    if (endcolor == NULL) {
703
		live_syntax->color = newcolor;
704
#ifdef DEBUG
705
		fprintf(stderr, "Starting a new colorstring for fg %hd, bg %hd\n", fg, bg);
706
#endif
707
	    } else {
Chris Allegretta's avatar
Chris Allegretta committed
708
#ifdef DEBUG
709
		fprintf(stderr, "Adding new entry for fg %hd, bg %hd\n", fg, bg);
Chris Allegretta's avatar
Chris Allegretta committed
710
#endif
711
712
		/* Need to recompute endcolor now so we can extend
		 * colors to syntaxes. */
713
		for (endcolor = live_syntax->color; endcolor->next != NULL;)
714
		    endcolor = endcolor->next;
715
		endcolor->next = newcolor;
716
	    }
717

718
	    endcolor = newcolor;
719
	} else
720
	    cancelled = TRUE;
Chris Allegretta's avatar
Chris Allegretta committed
721

722
	if (expectend) {
723
	    if (ptr == NULL || strncasecmp(ptr, "end=", 4) != 0) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
724
725
		rcfile_error(
			N_("\"start=\" requires a corresponding \"end=\""));
726
727
728
729
		return;
	    }
	    ptr += 4;
	    if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
730
731
		rcfile_error(
			N_("Regex strings must begin and end with a \" character"));
732
		continue;
733
	    }
734

735
	    fgstr = ++ptr;
736
	    ptr = parse_next_regex(ptr);
737
738
	    if (ptr == NULL)
		break;
739

740
741
	    /* If the start regex was invalid, skip past the end regex
	     * to stay in sync. */
742
743
	    if (cancelled)
		continue;
744

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

749
	    /* Lame way to skip another static counter. */
750
751
	    newcolor->id = live_syntax->nmultis;
	    live_syntax->nmultis++;
Chris Allegretta's avatar
Chris Allegretta committed
752
	}
753
    }
754
}
755

756
757
758
759
760
761
/* 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)
762
	return FALSE;
763
764
765
766
767
768
769
770
771
772
773
774
775

    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);
776
	    return FALSE;
777
778
779
780
781
782
783
784
785
786
	}
	*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)
787
	    return FALSE;
788
789
790
    } else
	*fg = -1;

791
    return TRUE;
792
793
}

794
795
/* Read regex strings enclosed in double quotes from the line pointed at
 * by ptr, and store them quoteless in the passed storage place. */
796
void grab_and_store(const char *kind, char *ptr, regexlisttype **storage)
797
{
798
    regexlisttype *lastthing;
799

800
    /* The default syntax doesn't take any file matching stuff. */
801
    if (strcmp(live_syntax->name, "default") == 0 && *ptr != '\0') {
802
803
804
805
806
	rcfile_error(
		N_("The \"default\" syntax does not accept '%s' regexes"), kind);
	return;
    }

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

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

818
819
820
821
822
823
    lastthing = *storage;

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

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

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

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

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

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

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

854
	lastthing = newthing;
855
856
    }
}
857

858
859
/* Parse and store the name given after a linter/formatter command. */
void pick_up_name(const char *kind, char *ptr, char **storage)
860
861
862
{
    assert(ptr != NULL);

863
    if (!opensyntax) {
864
	rcfile_error(
865
		N_("A '%s' command requires a preceding 'syntax' command"), kind);
866
867
868
869
	return;
    }

    if (*ptr == '\0') {
870
	rcfile_error(N_("Missing command after '%s'"), kind);
871
872
873
	return;
    }

874
    free(*storage);
875

876
    /* Allow unsetting the command by using an empty string. */
877
    if (!strcmp(ptr, "\"\""))
878
	*storage = NULL;
879
    else
880
	*storage = mallocstrcpy(NULL, ptr);
881
}
882
#endif /* !DISABLE_COLOR */
883

884
/* Check whether the user has unmapped every shortcut for a
885
 * sequence we consider 'vital', like the exit function. */
886
887
888
889
890
static void check_vitals_mapped(void)
{
    subnfunc *f;
    int v;
#define VITALS 5
891
    void (*vitals[VITALS])(void) = { do_exit, do_exit, do_cancel, do_cancel, do_cancel };
892
893
894
    int inmenus[VITALS] = { MMAIN, MHELP, MWHEREIS, MREPLACE, MGOTOLINE };

    for  (v = 0; v < VITALS; v++) {
895
896
897
898
899
	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 "
900
				     "\"%s\".  Exiting.\n"), f->desc);
901
		    fprintf(stderr, _("If needed, use nano with the -I option "
902
				     "to adjust your nanorc settings.\n"));
903
904
905
906
907
		     exit(1);
		}
		break;
	    }
	}
908
909
910
    }
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
911
912
/* Parse the rcfile, once it has been opened successfully at rcstream,
 * and close it afterwards.  If syntax_only is TRUE, only allow the file
913
 * to contain color syntax commands. */
914
void parse_rcfile(FILE *rcstream
915
#ifndef DISABLE_COLOR
916
917
918
	, bool syntax_only
#endif
	)
919
{
920
921
    char *buf = NULL;
    ssize_t len;
922
    size_t n = 0;
923
924
925

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
929
	/* Ignore the newline. */
930
931
	if (buf[len - 1] == '\n')
	    buf[len - 1] = '\0';
932
933
934

	lineno++;
	ptr = buf;
935
	while (isblank(*ptr))
936
937
	    ptr++;

938
939
940
	/* If we have a blank line or a comment, skip to the next
	 * line. */
	if (*ptr == '\0' || *ptr == '#')
941
942
	    continue;

943
	/* Otherwise, skip to the next space. */
944
945
946
	keyword = ptr;
	ptr = parse_next_word(ptr);

947
#ifndef DISABLE_COLOR
948
949
	/* Handle extending first... */
	if (strcasecmp(keyword, "extendsyntax") == 0) {
950
	    syntaxtype *sint;
951
952
953
	    char *syntaxname = ptr;

	    ptr = parse_next_word(ptr);
954
955
956

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

959
960
961
	    if (sint == NULL) {
		rcfile_error(N_("Could not find syntax \"%s\" to extend"),
				syntaxname);
962
		opensyntax = FALSE;
963
964
		continue;
	    }
965
966
967
968
969
970

	    live_syntax = sint;
	    opensyntax = TRUE;

	    keyword = ptr;
	    ptr = parse_next_word(ptr);
971
	}
972
#endif
973

974
	/* Try to parse the keyword. */
975
	if (strcasecmp(keyword, "set") == 0) {
976
#ifndef DISABLE_COLOR
977
	    if (syntax_only)
978
979
		rcfile_error(N_("Command \"%s\" not allowed in included file"),
				keyword);
980
981
982
983
	    else
#endif
		set = 1;
	} else if (strcasecmp(keyword, "unset") == 0) {
984
#ifndef DISABLE_COLOR
985
	    if (syntax_only)
986
987
		rcfile_error(N_("Command \"%s\" not allowed in included file"),
				keyword);
988
989
990
991
	    else
#endif
		set = -1;
	}
992
#ifndef DISABLE_COLOR
993
994
	else if (strcasecmp(keyword, "include") == 0) {
	    if (syntax_only)
995
996
		rcfile_error(N_("Command \"%s\" not allowed in included file"),
				keyword);
997
998
	    else
		parse_include(ptr);
999
	} else if (strcasecmp(keyword, "syntax") == 0) {
1000
	    if (opensyntax && endcolor == NULL)
1001
		rcfile_error(N_("Syntax \"%s\" has no color commands"),
1002
				live_syntax->name);
Chris Allegretta's avatar
Chris Allegretta committed
1003
	    parse_syntax(ptr);
1004
	}
1005
1006
	else if (strcasecmp(keyword, "header") == 0)
	    grab_and_store("header", ptr, &live_syntax->headers);
1007
	else if (strcasecmp(keyword, "magic") == 0)
1008
#ifdef HAVE_LIBMAGIC
1009
	    grab_and_store("magic", ptr, &live_syntax->magics);
1010
1011
1012
#else
	    ;
#endif
1013
	else if (strcasecmp(keyword, "color") == 0)
1014
1015
1016
	    parse_colors(ptr, FALSE);
	else if (strcasecmp(keyword, "icolor") == 0)
	    parse_colors(ptr, TRUE);
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
#endif /* !DISABLE_COLOR */
1026
	else if (strcasecmp(keyword, "bind") == 0)
1027
	    parse_binding(ptr, TRUE);
1028
	else if (strcasecmp(keyword, "unbind") == 0)
1029
	    parse_binding(ptr, FALSE);
1030
	else
1031
	    rcfile_error(N_("Command \"%s\" not understood"), keyword);
1032

1033
#ifndef DISABLE_COLOR
1034
1035
	/* If a syntax was extended, it stops at the end of the command. */
	if (live_syntax != syntaxes)
1036
	    opensyntax = FALSE;
1037
1038
#endif

1039
1040
1041
1042
	if (set == 0)
	    continue;

	if (*ptr == '\0') {
1043
	    rcfile_error(N_("Missing option"));
1044
1045
1046
1047
1048
1049
	    continue;
	}

	option = ptr;
	ptr = parse_next_word(ptr);

1050
1051
	for (i = 0; rcopts[i].name != NULL; i++) {
	    if (strcasecmp(option, rcopts[i].name) == 0) {
1052
#ifdef DEBUG
1053
1054
1055
1056
1057
1058
1059
1060
1061
		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
1062
			 * an argument. */
1063
			if (*ptr == '\0') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1064
			    rcfile_error(
1065
				N_("Option \"%s\" requires an argument"),
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1066
				rcopts[i].name);
1067
1068
1069
1070
1071
1072
			    break;
			}
			option = ptr;
			if (*option == '"')
			    option++;
			ptr = parse_argument(ptr);
1073

1074
			option = mallocstrcpy(NULL, option);
Chris Allegretta's avatar
Chris Allegretta committed
1075
#ifdef DEBUG
1076
			fprintf(stderr, "option = \"%s\"\n", option);
Chris Allegretta's avatar
Chris Allegretta committed
1077
#endif
1078
1079
1080
1081
1082
1083
1084
1085
1086

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

1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
#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
1098
#ifndef DISABLE_OPERATINGDIR
1099
			if (strcasecmp(rcopts[i].name, "operatingdir") == 0)
1100
			    operating_dir = option;
1101
			else
Chris Allegretta's avatar
Chris Allegretta committed
1102
#endif
1103
#ifndef DISABLE_WRAPJUSTIFY
1104
1105
			if (strcasecmp(rcopts[i].name, "fill") == 0) {
			    if (!parse_num(option, &wrap_at)) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1106
				rcfile_error(
1107
					N_("Requested fill size \"%s\" is invalid"),
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1108
					option);
1109
				wrap_at = -CHARS_FROM_EOL;
1110
1111
			    } else
				free(option);
1112
			} else
Chris Allegretta's avatar
Chris Allegretta committed
1113
#endif
1114
#ifndef NANO_TINY
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
			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) {
1126
			    whitespace = option;
1127
1128
			    if (mbstrlen(whitespace) != 2 ||
				strlenpt(whitespace) != 2) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1129
1130
				rcfile_error(
					N_("Two single-column characters required"));
1131
1132
				free(whitespace);
				whitespace = NULL;
1133
1134
1135
			    } else {
				whitespace_len[0] =
					parse_mbchar(whitespace, NULL,
1136
					NULL);
1137
1138
				whitespace_len[1] =
					parse_mbchar(whitespace +
1139
					whitespace_len[0], NULL, NULL);
1140
1141
			    }
			} else
1142
#endif
1143
#ifndef DISABLE_JUSTIFY
1144
			if (strcasecmp(rcopts[i].name, "punct") == 0) {
1145
			    punct = option;
1146
			    if (has_blank_mbchars(punct)) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1147
				rcfile_error(
1148
					N_("Non-blank characters required"));
1149
1150
1151
				free(punct);
				punct = NULL;
			    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1152
1153
			} else if (strcasecmp(rcopts[i].name,
				"brackets") == 0) {
1154
			    brackets = option;
1155
			    if (has_blank_mbchars(brackets)) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1156
				rcfile_error(
1157
					N_("Non-blank characters required"));
1158
1159
1160
				free(brackets);
				brackets = NULL;
			    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1161
1162
			} else if (strcasecmp(rcopts[i].name,
				"quotestr") == 0)
1163
			    quotestr = option;
1164
			else
1165
#endif
1166
#ifndef NANO_TINY
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1167
1168
			if (strcasecmp(rcopts[i].name,
				"backupdir") == 0)
1169
			    backup_dir = option;
1170
			else
1171
#endif
1172
#ifndef DISABLE_SPELLER
1173
			if (strcasecmp(rcopts[i].name, "speller") == 0)
1174
			    alt_speller = option;
1175
1176
			else
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1177
1178
1179
1180
1181
			if (strcasecmp(rcopts[i].name,
				"tabsize") == 0) {
			    if (!parse_num(option, &tabsize) ||
				tabsize <= 0) {
				rcfile_error(
1182
					N_("Requested tab size \"%s\" is invalid"),
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1183
					option);
1184
				tabsize = -1;
1185
1186
			    } else
				free(option);
1187
			} else
1188
1189
			    assert(FALSE);
		    }
1190
#ifdef DEBUG
1191
		    fprintf(stderr, "flag = %ld\n", rcopts[i].flag);
1192
#endif
1193
1194
1195
		} else if (rcopts[i].flag != 0)
		    UNSET(rcopts[i].flag);
		else
1196
		    rcfile_error(N_("Cannot unset option \"%s\""),
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1197
			rcopts[i].name);
1198
		break;
1199
1200
	    }
	}
1201
	if (rcopts[i].name == NULL)
1202
	    rcfile_error(N_("Unknown option \"%s\""), option);
1203
    }
1204

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

1211
1212
    opensyntax = FALSE;

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

Chris Allegretta's avatar
Chris Allegretta committed
1217
    check_vitals_mapped();
1218
1219
1220
    return;
}

1221
/* The main rcfile function.  It tries to open the system-wide rcfile,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1222
 * followed by the current user's rcfile. */
1223
1224
void do_rcfile(void)
{
1225
    struct stat rcinfo;
1226
1227
    FILE *rcstream;

1228
    nanorc = mallocstrcpy(nanorc, SYSCONFDIR "/nanorc");
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238

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

1239
1240
1241
1242
#ifdef DEBUG
    fprintf(stderr, "Parsing file \"%s\"\n", nanorc);
#endif

1243
    /* Try to open the system-wide nanorc. */
1244
    rcstream = fopen(nanorc, "rb");
1245
    if (rcstream != NULL)
1246
	parse_rcfile(rcstream
1247
#ifndef DISABLE_COLOR
1248
1249
1250
		, FALSE
#endif
		);
1251

1252
#ifdef DISABLE_ROOTWRAPPING
1253
    /* We've already read SYSCONFDIR/nanorc, if it's there.  If we're
1254
1255
     * root, and --disable-wrapping-as-root is used, turn wrapping off
     * now. */
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
1267
1268
1269
#ifndef RCFILE_NAME
#define RCFILE_NAME ".nanorc"
#endif
	nanorc = charealloc(nanorc, strlen(homedir) + strlen(RCFILE_NAME) + 2);
	sprintf(nanorc, "%s/%s", homedir, RCFILE_NAME);
1270

1271
1272
1273
	/* 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) ||
1274
			S_ISBLK(rcinfo.st_mode))
1275
1276
1277
1278
1279
1280
1281
		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");
1282
	if (rcstream == NULL) {
1283
1284
	    /* Don't complain about the file's not existing. */
	    if (errno != ENOENT)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1285
		rcfile_error(N_("Error reading %s: %s"), nanorc,
1286
				strerror(errno));
1287
	} else
1288
	    parse_rcfile(rcstream
1289
#ifndef DISABLE_COLOR
1290
1291
1292
		, FALSE
#endif
		);
1293
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1294

1295
1296
    free(nanorc);
    nanorc = NULL;
1297

1298
1299
    if (errors && !ISSET(QUIET)) {
	errors = FALSE;
1300
	fprintf(stderr, _("\nPress Enter to continue starting nano.\n"));
1301
1302
1303
	while (getchar() != '\n')
	    ;
    }
1304
1305
}

1306
#endif /* !DISABLE_NANORC */