rcfile.c 33.8 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
    {"const", CONST_UPDATE},
Chris Allegretta's avatar
Chris Allegretta committed
42
#ifndef DISABLE_WRAPJUSTIFY
43
    {"fill", 0},
Chris Allegretta's avatar
Chris Allegretta committed
44
#endif
45
46
47
#ifndef NANO_TINY
    {"locking", LOCKING},
#endif
48
#ifndef DISABLE_MOUSE
49
    {"mouse", USE_MOUSE},
50
#endif
51
#ifndef DISABLE_MULTIBUFFER
52
53
    {"multibuffer", MULTIBUFFER},
#endif
54
    {"morespace", MORE_SPACE},
55
    {"nofollow", NOFOLLOW_SYMLINKS},
56
    {"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
#endif
Chris Allegretta's avatar
Chris Allegretta committed
64
    {"preserve", PRESERVE},
65
#ifndef DISABLE_JUSTIFY
66
    {"punct", 0},
67
68
    {"quotestr", 0},
#endif
69
    {"rebinddelete", REBIND_DELETE},
70
    {"rebindkeypad", REBIND_KEYPAD},
71
72
73
#ifdef HAVE_REGEX_H
    {"regexp", USE_REGEXP},
#endif
74
#ifndef DISABLE_SPELLER
75
    {"speller", 0},
76
77
78
#endif
    {"suspend", SUSPEND},
    {"tabsize", 0},
79
    {"tempfile", TEMP_FILE},
80
    {"view", VIEW_MODE},
81
#ifndef NANO_TINY
82
83
    {"autoindent", AUTOINDENT},
    {"backup", BACKUP_FILE},
84
    {"allow_insecure_backup", INSECURE_BACKUP},
85
86
87
88
89
    {"backupdir", 0},
    {"backwards", BACKWARDS_SEARCH},
    {"casesensitive", CASE_SENSITIVE},
    {"cut", CUT_TO_END},
    {"historylog", HISTORYLOG},
90
    {"matchbrackets", 0},
91
    {"noconvert", NO_CONVERT},
92
    {"poslog", POS_HISTORY},
93
    {"quiet", QUIET},
94
    {"quickblank", QUICK_BLANK},
95
96
97
    {"smarthome", SMART_HOME},
    {"smooth", SMOOTH_SCROLL},
    {"tabstospaces", TABS_TO_SPACES},
98
    {"undo", UNDOABLE},
99
    {"whitespace", 0},
100
    {"wordbounds", WORD_BOUNDS},
101
    {"softwrap", SOFTWRAP},
102
103
104
105
106
107
#endif
#ifndef DISABLE_COLOR
    {"titlecolor", 0},
    {"statuscolor", 0},
    {"keycolor", 0},
    {"functioncolor", 0},
108
#endif
109
    {NULL, 0}
110
};
111

112
static bool errors = FALSE;
113
	/* Whether we got any errors while parsing an rcfile. */
114
static size_t lineno = 0;
115
	/* If we did, the line number where the last error occurred. */
116
static char *nanorc = NULL;
117
	/* The path to the rcfile we're parsing. */
118
#ifndef DISABLE_COLOR
119
120
121
122
123
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
124

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
125
126
127
/* 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
128
void rcfile_error(const char *msg, ...)
129
130
131
{
    va_list ap;

132
133
134
    if (ISSET(QUIET))
	return;

135
    fprintf(stderr, "\n");
136
137
    if (lineno > 0) {
	errors = TRUE;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
138
	fprintf(stderr, _("Error in %s on line %lu: "), nanorc, (unsigned long)lineno);
139
    }
Chris Allegretta's avatar
Chris Allegretta committed
140

141
    va_start(ap, msg);
142
    vfprintf(stderr, _(msg), ap);
143
    va_end(ap);
144
145

    fprintf(stderr, "\n");
146
147
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
148
149
150
/* 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
151
char *parse_next_word(char *ptr)
152
{
153
    while (!isblank(*ptr) && *ptr != '\0')
154
155
156
	ptr++;

    if (*ptr == '\0')
157
	return ptr;
158

159
160
    /* Null-terminate and advance ptr. */
    *ptr++ = '\0';
161

162
    while (isblank(*ptr))
163
164
165
166
	ptr++;

    return ptr;
}
167

168
169
170
171
/* 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
172
173
char *parse_argument(char *ptr)
{
174
    const char *ptr_save = ptr;
Chris Allegretta's avatar
Chris Allegretta committed
175
176
177
178
179
180
181
182
183
184
185
    char *last_quote = NULL;

    assert(ptr != NULL);

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

    do {
	ptr++;
	if (*ptr == '"')
	    last_quote = ptr;
186
    } while (*ptr != '\0');
Chris Allegretta's avatar
Chris Allegretta committed
187
188
189
190
191
192

    if (last_quote == NULL) {
	if (*ptr == '\0')
	    ptr = NULL;
	else
	    *ptr++ = '\0';
193
	rcfile_error(N_("Argument '%s' has an unterminated \""), ptr_save);
Chris Allegretta's avatar
Chris Allegretta committed
194
195
196
197
198
    } else {
	*last_quote = '\0';
	ptr = last_quote + 1;
    }
    if (ptr != NULL)
199
	while (isblank(*ptr))
Chris Allegretta's avatar
Chris Allegretta committed
200
201
202
203
	    ptr++;
    return ptr;
}

204
#ifndef DISABLE_COLOR
205
/* Parse the next regex string from the line at ptr, and return it. */
206
207
char *parse_next_regex(char *ptr)
{
208
209
210
211
    assert(ptr != NULL);

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

216
217
218
    assert(*ptr == '"' || *ptr == '\0');

    if (*ptr == '\0') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
219
220
	rcfile_error(
		N_("Regex strings must begin and end with a \" character"));
221
	return NULL;
222
    }
223

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
224
    /* Null-terminate and advance ptr. */
225
226
    *ptr++ = '\0';

227
    while (isblank(*ptr))
228
229
230
231
232
	ptr++;

    return ptr;
}

233
234
/* Compile the regular expression regex to see if it's valid.  Return
 * TRUE if it is, or FALSE otherwise. */
235
bool nregcomp(const char *regex, int eflags)
236
{
237
    regex_t preg;
238
    const char *r = fixbounds(regex);
239
    int rc = regcomp(&preg, r, REG_EXTENDED | eflags);
240
241

    if (rc != 0) {
242
	size_t len = regerror(rc, &preg, NULL, 0);
243
244
	char *str = charalloc(len);

245
	regerror(rc, &preg, str, len);
246
	rcfile_error(N_("Bad regex \"%s\": %s"), r, str);
247
248
	free(str);
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
249

250
    regfree(&preg);
251
    return (rc == 0);
252
253
}

254
255
/* 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
256
void parse_syntax(char *ptr)
257
{
Chris Allegretta's avatar
Chris Allegretta committed
258
    const char *fileregptr = NULL, *nameptr = NULL;
259
    syntaxtype *tmpsyntax, *prev_syntax;
260
261
    exttype *endext = NULL;
	/* The end of the extensions list for this syntax. */
262

263
    assert(ptr != NULL);
264

265
266
    if (*ptr == '\0') {
	rcfile_error(N_("Missing syntax name"));
267
	return;
268
    }
269
270

    if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
271
272
	rcfile_error(
		N_("Regex strings must begin and end with a \" character"));
273
	return;
274
    }
275

276
277
278
279
280
    ptr++;

    nameptr = ptr;
    ptr = parse_next_regex(ptr);

281
    if (ptr == NULL)
282
	return;
283

284
285
    /* Search for a duplicate syntax name.  If we find one, free it, so
     * that we always use the last syntax with a given name. */
286
    prev_syntax = NULL;
287
288
    for (tmpsyntax = syntaxes; tmpsyntax != NULL;
	tmpsyntax = tmpsyntax->next) {
289
	if (strcmp(nameptr, tmpsyntax->desc) == 0) {
290
291
292
293
	    syntaxtype *old_syntax = tmpsyntax;

	    if (endsyntax == tmpsyntax)
		endsyntax = prev_syntax;
294
295

	    tmpsyntax = tmpsyntax->next;
296
297
298
299
300
301
302
	    if (prev_syntax != NULL)
		prev_syntax->next = tmpsyntax;
	    else
		syntaxes = tmpsyntax;

	    free(old_syntax->desc);
	    free(old_syntax);
303
	    break;
304
	}
305
	prev_syntax = tmpsyntax;
306
307
    }

Chris Allegretta's avatar
Chris Allegretta committed
308
309
    if (syntaxes == NULL) {
	syntaxes = (syntaxtype *)nmalloc(sizeof(syntaxtype));
310
	endsyntax = syntaxes;
Chris Allegretta's avatar
Chris Allegretta committed
311
    } else {
312
313
	endsyntax->next = (syntaxtype *)nmalloc(sizeof(syntaxtype));
	endsyntax = endsyntax->next;
314
#ifdef DEBUG
315
	fprintf(stderr, "Adding new syntax after first one\n");
316
#endif
Chris Allegretta's avatar
Chris Allegretta committed
317
    }
318

319
320
321
322
    endsyntax->desc = mallocstrcpy(NULL, nameptr);
    endsyntax->color = NULL;
    endcolor = NULL;
    endsyntax->extensions = NULL;
323
    endsyntax->headers = NULL;
324
    endsyntax->magics = NULL;
325
    endsyntax->next = NULL;
326
    endsyntax->nmultis = 0;
327
    endsyntax->linter = NULL;
328

329
#ifdef DEBUG
330
    fprintf(stderr, "Starting a new syntax type: \"%s\"\n", nameptr);
331
332
#endif

333
334
335
336
337
338
339
    /* The "none" syntax is the same as not having a syntax at all, so
     * we can't assign any extensions or colors to it. */
    if (strcmp(endsyntax->desc, "none") == 0) {
	rcfile_error(N_("The \"none\" syntax is reserved"));
	return;
    }

340
    /* The default syntax should have no associated extensions. */
341
    if (strcmp(endsyntax->desc, "default") == 0 && *ptr != '\0') {
342
343
	rcfile_error(
		N_("The \"default\" syntax must take no extensions"));
344
345
346
	return;
    }

347
    /* Now load the extension regexes into their part of the struct. */
348
    while (*ptr != '\0') {
349
350
	exttype *newext;

351
	while (*ptr != '"' && *ptr != '\0')
352
353
	    ptr++;

354
	if (*ptr == '\0')
355
	    return;
356

357
358
359
360
	ptr++;

	fileregptr = ptr;
	ptr = parse_next_regex(ptr);
361
362
	if (ptr == NULL)
	    break;
363

364
	newext = (exttype *)nmalloc(sizeof(exttype));
365
366
367
368
369
370

	/* Save the extension regex if it's valid. */
	if (nregcomp(fileregptr, REG_NOSUB)) {
	    newext->ext_regex = mallocstrcpy(NULL, fileregptr);
	    newext->ext = NULL;

371
	    if (endext == NULL)
372
		endsyntax->extensions = newext;
373
374
375
376
	    else
		endext->next = newext;
	    endext = newext;
	    endext->next = NULL;
377
378
	} else
	    free(newext);
379
    }
380
381
}

382
/* Parse the magic regexes that may influence the choice of syntax. */
383
384
385
386
void parse_magictype(char *ptr)
{
#ifdef HAVE_LIBMAGIC
    const char *fileregptr = NULL;
387
    exttype *endmagic = NULL;
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411

    assert(ptr != NULL);

    if (syntaxes == NULL) {
	rcfile_error(
		N_("Cannot add a magic string regex without a syntax command"));
	return;
    }

    if (*ptr == '\0') {
	rcfile_error(N_("Missing magic string name"));
	return;
    }

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

#ifdef DEBUG
    fprintf(stderr, "Starting a magic type: \"%s\"\n", ptr);
#endif

412
    /* Now load the magic regexes into their part of the struct. */
413
    while (*ptr != '\0') {
414
	exttype *newmagic;
415
416
417
418
419
420
421
422
423
424
425
426
427
428

	while (*ptr != '"' && *ptr != '\0')
	    ptr++;

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

	ptr++;

	fileregptr = ptr;
	ptr = parse_next_regex(ptr);
	if (ptr == NULL)
	    break;

429
	newmagic = (exttype *)nmalloc(sizeof(exttype));
430

431
	/* Save the regex string if it's valid. */
432
	if (nregcomp(fileregptr, REG_NOSUB)) {
433
434
	    newmagic->ext_regex = mallocstrcpy(NULL, fileregptr);
	    newmagic->ext = NULL;
435

436
437
	    if (endmagic == NULL)
		endsyntax->magics = newmagic;
438
	    else
439
440
441
		endmagic->next = newmagic;
	    endmagic = newmagic;
	    endmagic->next = NULL;
442
	} else
443
	    free(newmagic);
444
445
    }
#endif /* HAVE_LIBMAGIC */
446
}
447
#endif /* !DISABLE_COLOR */
448

449

450
451
452
453
454
455
456
457
int check_bad_binding(sc *s)
{
#define BADLISTLEN 1
    int badtypes[BADLISTLEN] = {META};
    int badseqs[BADLISTLEN] = { 91 };
    int i;

    for (i = 0; i < BADLISTLEN; i++)
458
	if (s->type == badtypes[i] && s->seq == badseqs[i])
459
460
461
462
463
	    return 1;

    return 0;
}

464
/* Bind or unbind a key combo, to or from a function. */
465
void parse_binding(char *ptr, bool dobind)
466
467
{
    char *keyptr = NULL, *keycopy = NULL, *funcptr = NULL, *menuptr = NULL;
468
    sc *s, *newsc = NULL;
469
    int menu;
470
471
472

    assert(ptr != NULL);

473
474
475
476
#ifdef DEBUG
    fprintf(stderr, "Starting the rebinding code...\n");
#endif

477
478
479
480
481
482
483
484
    if (*ptr == '\0') {
	rcfile_error(N_("Missing key name"));
	return;
    }

    keyptr = ptr;
    ptr = parse_next_word(ptr);
    keycopy = mallocstrcpy(NULL, keyptr);
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501

    if (strlen(keycopy) < 2) {
	rcfile_error(N_("Key name is too short"));
	return;
    }

    /* Uppercase only the first two or three characters of the key name. */
    keycopy[0] = toupper(keycopy[0]);
    keycopy[1] = toupper(keycopy[1]);
    if (keycopy[0] == 'M' && keycopy[1] == '-') {
	if (strlen(keycopy) > 2)
	    keycopy[2] = toupper(keycopy[2]);
	else {
	    rcfile_error(N_("Key name is too short"));
	    return;
	}
    }
502

503
504
505
506
507
    /* 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') {
508
	rcfile_error(N_("Key name must begin with \"^\", \"M\", or \"F\""));
509
510
511
	return;
    }

512
513
514
    if (dobind) {
	funcptr = ptr;
	ptr = parse_next_word(ptr);
515

516
	if (!strcmp(funcptr, "")) {
517
	    rcfile_error(N_("Must specify a function to bind the key to"));
518
519
	    return;
	}
520
521
522
523
524
    }

    menuptr = ptr;
    ptr = parse_next_word(ptr);

525
    if (!strcmp(menuptr, "")) {
526
	/* TRANSLATORS: Do not translate the word "all". */
527
	rcfile_error(N_("Must specify a menu (or \"all\") in which to bind/unbind the key"));
528
529
	return;
    }
530
531

    if (dobind) {
532
	newsc = strtosc(funcptr);
533
	if (newsc == NULL) {
534
	    rcfile_error(N_("Cannot map name \"%s\" to a function"), funcptr);
535
536
	    return;
	}
537
538
    }

539
540
541
542
543
544
    menu = strtomenu(menuptr);
    if (menu < 1) {
	rcfile_error(N_("Cannot map name \"%s\" to a menu"), menuptr);
	return;
    }

545
#ifdef DEBUG
546
    if (dobind)
547
548
	fprintf(stderr, "newsc address is now %ld, assigned func = %ld, menu = %x\n",
	    (long)&newsc, (long)newsc->scfunc, menu);
549
550
   else
	fprintf(stderr, "unbinding \"%s\" from menu %x\n", keycopy, menu);
551
552
#endif

553
554
555
556
557
    if (dobind) {
	newsc->keystr = keycopy;
	newsc->menu = menu;
	newsc->type = strtokeytype(newsc->keystr);
	assign_keyinfo(newsc);
558
#ifdef DEBUG
559
560
	fprintf(stderr, "s->keystr = \"%s\"\n", newsc->keystr);
	fprintf(stderr, "s->seq = \"%d\"\n", newsc->seq);
561
562
#endif

563
564
565
	if (check_bad_binding(newsc)) {
	    rcfile_error(N_("Sorry, keystr \"%s\" is an illegal binding"), newsc->keystr);
	    return;
566
567
	}
    }
568

569
    /* Now find and delete any existing same shortcut in the menu(s). */
570
    for (s = sclist; s != NULL; s = s->next) {
571
	if (((s->menu & menu)) && !strcmp(s->keystr, keycopy)) {
572
#ifdef DEBUG
573
	    fprintf(stderr, "deleting entry from menu %x\n", s->menu);
574
#endif
575
	    s->menu &= ~menu;
576
577
	}
    }
578
579
580
581
582
583

    if (dobind) {
	/* Add the new shortcut at the start of the list. */
	newsc->next = sclist;
	sclist = newsc;
    }
584
585
}

586

587
#ifndef DISABLE_COLOR
588
/* Read and parse additional syntax files. */
589
static void _parse_include(char *file)
590
591
592
593
{
    struct stat rcinfo;
    FILE *rcstream;

594
595
596
    /* 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). */
597
598

    /* Don't open directories, character files, or block files. */
599
    if (stat(file, &rcinfo) != -1) {
600
601
602
603
	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") :
604
		_("\"%s\" is a device file"), file);
605
606
607
608
	}
    }

    /* Open the new syntax file. */
609
610
    if ((rcstream = fopen(file, "rb")) == NULL) {
	rcfile_error(_("Error reading %s: %s"), file,
611
		strerror(errno));
612
	return;
613
614
615
616
    }

    /* 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. */
617
    nanorc = file;
618
619
    lineno = 0;

620
#ifdef DEBUG
621
    fprintf(stderr, "Parsing file \"%s\"\n", file);
622
623
#endif

624
    parse_rcfile(rcstream
625
#ifndef DISABLE_COLOR
626
627
628
	, TRUE
#endif
	);
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
}

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

653
654
655
    globfree(&files);
    free(expanded);

656
657
658
659
660
661
    /* We're done with the new syntax file.  Restore the original
     * filename and line number position. */
    nanorc = nanorc_save;
    lineno = lineno_save;
}

662
663
/* Return the short value corresponding to the color named in colorname,
 * and set bright to TRUE if that color is bright. */
664
short color_to_short(const char *colorname, bool *bright)
665
{
666
    short mcolor = -1;
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700

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

701
702
703
/* 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. */
704
void parse_colors(char *ptr, bool icase)
705
{
706
    short fg, bg;
707
    bool bright = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
708
    char *fgstr;
709

710
    assert(ptr != NULL);
711

712
713
    if (syntaxes == NULL) {
	rcfile_error(
714
		N_("Cannot add a color command without a syntax command"));
715
716
717
	return;
    }

718
    if (*ptr == '\0') {
719
	rcfile_error(N_("Missing color name"));
720
	return;
721
722
    }

723
724
    fgstr = ptr;
    ptr = parse_next_word(ptr);
725
726
    if (!parse_color_names(fgstr, &fg, &bg, &bright))
	return;
727

728
    if (*ptr == '\0') {
729
	rcfile_error(N_("Missing regex string"));
730
731
732
	return;
    }

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
733
    /* Now for the fun part.  Start adding regexes to individual strings
734
735
     * in the colorstrings array, woo! */
    while (ptr != NULL && *ptr != '\0') {
736
	colortype *newcolor;
737
	    /* The container for a color plus its regexes. */
738
	bool cancelled = FALSE;
739
	    /* The start expression was bad. */
740
741
	bool expectend = FALSE;
	    /* Do we expect an end= line? */
742

743
	if (strncasecmp(ptr, "start=", 6) == 0) {
744
	    ptr += 6;
745
	    expectend = TRUE;
746
747
748
	}

	if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
749
750
	    rcfile_error(
		N_("Regex strings must begin and end with a \" character"));
751
	    ptr = parse_next_regex(ptr);
752
753
	    continue;
	}
754

755
	ptr++;
756

757
758
	fgstr = ptr;
	ptr = parse_next_regex(ptr);
759
760
761
	if (ptr == NULL)
	    break;

762
	newcolor = (colortype *)nmalloc(sizeof(colortype));
763

764
765
766
	/* Save the starting regex string if it's valid, and set up the
	 * color information. */
	if (nregcomp(fgstr, icase ? REG_ICASE : 0)) {
767
768
769
	    newcolor->fg = fg;
	    newcolor->bg = bg;
	    newcolor->bright = bright;
770
	    newcolor->icase = icase;
771
772
773
774

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

775
	    newcolor->end_regex = NULL;
776
	    newcolor->end = NULL;
777

778
	    newcolor->next = NULL;
779

780
781
	    if (endcolor == NULL) {
		endsyntax->color = newcolor;
782
#ifdef DEBUG
783
		fprintf(stderr, "Starting a new colorstring for fg %hd, bg %hd\n", fg, bg);
784
#endif
785
	    } else {
Chris Allegretta's avatar
Chris Allegretta committed
786
#ifdef DEBUG
787
		fprintf(stderr, "Adding new entry for fg %hd, bg %hd\n", fg, bg);
Chris Allegretta's avatar
Chris Allegretta committed
788
#endif
789
790
		/* Need to recompute endcolor now so we can extend
		 * colors to syntaxes. */
791
		for (endcolor = endsyntax->color; endcolor->next != NULL; endcolor = endcolor->next)
792
		    ;
793
		endcolor->next = newcolor;
794
	    }
795

796
	    endcolor = newcolor;
797
798
799
	} else {
	    free(newcolor);
	    cancelled = TRUE;
800
	}
Chris Allegretta's avatar
Chris Allegretta committed
801

802
	if (expectend) {
803
	    if (ptr == NULL || strncasecmp(ptr, "end=", 4) != 0) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
804
805
		rcfile_error(
			N_("\"start=\" requires a corresponding \"end=\""));
806
807
808
809
		return;
	    }
	    ptr += 4;
	    if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
810
811
		rcfile_error(
			N_("Regex strings must begin and end with a \" character"));
812
		continue;
813
	    }
814

815
816
	    ptr++;

817
	    fgstr = ptr;
818
	    ptr = parse_next_regex(ptr);
819
820
	    if (ptr == NULL)
		break;
821

822
823
	    /* If the start regex was invalid, skip past the end regex
	     * to stay in sync. */
824
825
	    if (cancelled)
		continue;
826

827
828
829
	    /* Save the ending regex string if it's valid. */
	    newcolor->end_regex = (nregcomp(fgstr, icase ? REG_ICASE :
		0)) ? mallocstrcpy(NULL, fgstr) : NULL;
830

831
	    /* Lame way to skip another static counter. */
832
            newcolor->id = endsyntax->nmultis;
833
            endsyntax->nmultis++;
Chris Allegretta's avatar
Chris Allegretta committed
834
	}
835
    }
836
}
837

838
839
840
841
842
843
/* 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)
844
	return FALSE;
845
846
847
848
849
850
851
852
853
854
855
856
857

    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);
858
	    return FALSE;
859
860
861
862
863
864
865
866
867
868
	}
	*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)
869
	    return FALSE;
870
871
872
    } else
	*fg = -1;

873
    return TRUE;
874
875
}

876
/* Parse the header-line regexes that may influence the choice of syntax. */
877
878
void parse_headers(char *ptr)
{
879
    char *regstr;
880
    exttype *endheader = NULL;
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918

    assert(ptr != NULL);

    if (syntaxes == NULL) {
	rcfile_error(
		N_("Cannot add a header regex without a syntax command"));
	return;
    }

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

    while (ptr != NULL && *ptr != '\0') {
	exttype *newheader;

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

	ptr++;

	regstr = ptr;
	ptr = parse_next_regex(ptr);
	if (ptr == NULL)
	    break;

	newheader = (exttype *)nmalloc(sizeof(exttype));

	/* Save the regex string if it's valid */
	if (nregcomp(regstr, 0)) {
	    newheader->ext_regex = mallocstrcpy(NULL, regstr);
	    newheader->ext = NULL;

919
	    if (endheader == NULL)
920
		endsyntax->headers = newheader;
921
	    else
922
923
		endheader->next = newheader;
	    endheader = newheader;
924
	    endheader->next = NULL;
925
926
927
928
	} else
	    free(newheader);
    }
}
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948

/* Parse the linter requested for this syntax.  Simple? */
void parse_linter(char *ptr)
{
    assert(ptr != NULL);

    if (syntaxes == NULL) {
	rcfile_error(
		N_("Cannot add a linter without a syntax command"));
	return;
    }

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

    if (endsyntax->linter != NULL)
	free(endsyntax->linter);

949
950
951
952
953
    /* Let them unset the linter by using "" */
    if (!strcmp(ptr, "\"\""))
	endsyntax->linter = NULL;
    else
	endsyntax->linter = mallocstrcpy(syntaxes->linter, ptr);
954
}
955
#endif /* !DISABLE_COLOR */
956

957
/* Check whether the user has unmapped every shortcut for a
958
 * sequence we consider 'vital', like the exit function. */
959
960
961
962
963
static void check_vitals_mapped(void)
{
    subnfunc *f;
    int v;
#define VITALS 5
964
    void (*vitals[VITALS])(void) = { do_exit, do_exit, do_cancel, do_cancel, do_cancel };
965
966
967
968
969
970
971
    int inmenus[VITALS] = { MMAIN, MHELP, MWHEREIS, MREPLACE, MGOTOLINE };

    for  (v = 0; v < VITALS; v++) {
       for (f = allfuncs; f != NULL; f = f->next) {
           if (f->scfunc == vitals[v] && f->menus & inmenus[v]) {
               const sc *s = first_sc_for(inmenus[v], f->scfunc);
               if (!s) {
972
973
974
                   fprintf(stderr, _("Fatal error: no keys mapped for function "
				     "\"%s\".  Exiting.\n"), f->desc);
                   fprintf(stderr, _("If needed, use nano with the -I option "
975
				     "to adjust your nanorc settings.\n"));
976
977
978
979
980
981
982
983
                   exit(1);
               }
           break;
           }
       }
    }
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
984
985
986
/* 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. */
987
void parse_rcfile(FILE *rcstream
988
#ifndef DISABLE_COLOR
989
990
991
	, bool syntax_only
#endif
	)
992
{
993
994
    char *buf = NULL;
    ssize_t len;
995
    size_t n = 0;
996
#ifndef DISABLE_COLOR
997
998
    syntaxtype *end_syn_save = NULL;
#endif
999
1000
1001

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1005
	/* Ignore the newline. */
1006
1007
	if (buf[len - 1] == '\n')
	    buf[len - 1] = '\0';
1008
1009
1010

	lineno++;
	ptr = buf;
1011
	while (isblank(*ptr))
1012
1013
	    ptr++;

1014
1015
1016
	/* If we have a blank line or a comment, skip to the next
	 * line. */
	if (*ptr == '\0' || *ptr == '#')
1017
1018
	    continue;

1019
	/* Otherwise, skip to the next space. */
1020
1021
1022
	keyword = ptr;
	ptr = parse_next_word(ptr);

1023
#ifndef DISABLE_COLOR
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
	/* Handle extending first... */
	if (strcasecmp(keyword, "extendsyntax") == 0) {
	    char *syntaxname = ptr;
	    syntaxtype *ts = NULL;

	    ptr = parse_next_word(ptr);
	    for (ts = syntaxes; ts != NULL; ts = ts->next)
		if (!strcmp(ts->desc, syntaxname))
		    break;

	    if (ts == NULL) {
		rcfile_error(N_("Could not find syntax \"%s\" to extend"), syntaxname);
		continue;
	    } else {
		end_syn_save = endsyntax;
		endsyntax = ts;
		keyword = ptr;
		ptr = parse_next_word(ptr);
	    }
	}
1044
#endif
1045

1046
	/* Try to parse the keyword. */
1047
	if (strcasecmp(keyword, "set") == 0) {
1048
#ifndef DISABLE_COLOR
1049
1050
	    if (syntax_only)
		rcfile_error(
1051
			N_("Command \"%s\" not allowed in included file"),
1052
1053
1054
1055
1056
			keyword);
	    else
#endif
		set = 1;
	} else if (strcasecmp(keyword, "unset") == 0) {
1057
#ifndef DISABLE_COLOR
1058
1059
	    if (syntax_only)
		rcfile_error(
1060
			N_("Command \"%s\" not allowed in included file"),
1061
1062
1063
1064
1065
			keyword);
	    else
#endif
		set = -1;
	}
1066
#ifndef DISABLE_COLOR
1067
1068
1069
	else if (strcasecmp(keyword, "include") == 0) {
	    if (syntax_only)
		rcfile_error(
1070
			N_("Command \"%s\" not allowed in included file"),
1071
1072
1073
			keyword);
	    else
		parse_include(ptr);
1074
1075
	} else if (strcasecmp(keyword, "syntax") == 0) {
	    if (endsyntax != NULL && endcolor == NULL)
1076
		rcfile_error(N_("Syntax \"%s\" has no color commands"),
1077
			endsyntax->desc);
Chris Allegretta's avatar
Chris Allegretta committed
1078
	    parse_syntax(ptr);
1079
	}
1080
	else if (strcasecmp(keyword, "magic") == 0)
1081
	    parse_magictype(ptr);
1082
	else if (strcasecmp(keyword, "header") == 0)
1083
1084
	    parse_headers(ptr);
	else if (strcasecmp(keyword, "color") == 0)
1085
1086
1087
	    parse_colors(ptr, FALSE);
	else if (strcasecmp(keyword, "icolor") == 0)
	    parse_colors(ptr, TRUE);
1088
1089
	else if (strcasecmp(keyword, "linter") == 0)
	    parse_linter(ptr);
1090
#endif /* !DISABLE_COLOR */
1091
	else if (strcasecmp(keyword, "bind") == 0)
1092
	    parse_binding(ptr, TRUE);
1093
	else if (strcasecmp(keyword, "unbind") == 0)
1094
	    parse_binding(ptr, FALSE);
1095
	else
1096
	    rcfile_error(N_("Command \"%s\" not understood"), keyword);
1097

1098
1099
1100
#ifndef DISABLE_COLOR
	/* If we temporarily reset endsyntax to allow extending,
	 * restore the value here. */
1101
1102
1103
1104
1105
1106
	if (end_syn_save != NULL) {
	    endsyntax = end_syn_save;
	    end_syn_save = NULL;
	}
#endif

1107
1108
1109
1110
	if (set == 0)
	    continue;

	if (*ptr == '\0') {
1111
	    rcfile_error(N_("Missing option"));
1112
1113
1114
1115
1116
1117
	    continue;
	}

	option = ptr;
	ptr = parse_next_word(ptr);

1118
1119
	for (i = 0; rcopts[i].name != NULL; i++) {
	    if (strcasecmp(option, rcopts[i].name) == 0) {
1120
#ifdef DEBUG
1121
1122
1123
1124
1125
1126
1127
1128
1129
		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
1130
			 * an argument. */
1131
			if (*ptr == '\0') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1132
			    rcfile_error(
1133
				N_("Option \"%s\" requires an argument"),
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1134
				rcopts[i].name);
1135
1136
1137
1138
1139
1140
			    break;
			}
			option = ptr;
			if (*option == '"')
			    option++;
			ptr = parse_argument(ptr);
1141

1142
			option = mallocstrcpy(NULL, option);
Chris Allegretta's avatar
Chris Allegretta committed
1143
#ifdef DEBUG
1144
			fprintf(stderr, "option = \"%s\"\n", option);
Chris Allegretta's avatar
Chris Allegretta committed
1145
#endif
1146
1147
1148
1149
1150
1151
1152
1153
1154

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

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

1276
#ifndef DISABLE_COLOR
1277
    if (endsyntax != NULL && endcolor == NULL)
1278
	rcfile_error(N_("Syntax \"%s\" has no color commands"),
1279
		endsyntax->desc);
1280
#endif
1281

Chris Allegretta's avatar
Chris Allegretta committed
1282
    free(buf);
1283
1284
    fclose(rcstream);
    lineno = 0;
1285

Chris Allegretta's avatar
Chris Allegretta committed
1286
    check_vitals_mapped();
1287
1288
1289
    return;
}

1290
/* The main rcfile function.  It tries to open the system-wide rcfile,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1291
 * followed by the current user's rcfile. */
1292
1293
void do_rcfile(void)
{
1294
    struct stat rcinfo;
1295
1296
    FILE *rcstream;

1297
    nanorc = mallocstrcpy(nanorc, SYSCONFDIR "/nanorc");
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307

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

1308
1309
1310
1311
#ifdef DEBUG
    fprintf(stderr, "Parsing file \"%s\"\n", nanorc);
#endif

1312
    /* Try to open the system-wide nanorc. */
1313
    rcstream = fopen(nanorc, "rb");
1314
    if (rcstream != NULL)
1315
	parse_rcfile(rcstream
1316
#ifndef DISABLE_COLOR
1317
1318
1319
		, FALSE
#endif
		);
1320

1321
#ifdef DISABLE_ROOTWRAPPING
1322
    /* We've already read SYSCONFDIR/nanorc, if it's there.  If we're
1323
1324
     * root, and --disable-wrapping-as-root is used, turn wrapping off
     * now. */
1325
1326
1327
    if (geteuid() == NANO_ROOT_UID)
	SET(NO_WRAP);
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1328

1329
    get_homedir();
1330

1331
    if (homedir == NULL)
1332
	rcfile_error(N_("I can't find my home directory!  Wah!"));
1333
    else {
1334
1335
1336
1337
1338
#ifndef RCFILE_NAME
#define RCFILE_NAME ".nanorc"
#endif
	nanorc = charealloc(nanorc, strlen(homedir) + strlen(RCFILE_NAME) + 2);
	sprintf(nanorc, "%s/%s", homedir, RCFILE_NAME);
1339

1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
	/* 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");
1351
	if (rcstream == NULL) {
1352
1353
	    /* Don't complain about the file's not existing. */
	    if (errno != ENOENT)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1354
1355
		rcfile_error(N_("Error reading %s: %s"), nanorc,
			strerror(errno));
1356
	} else
1357
	    parse_rcfile(rcstream
1358
#ifndef DISABLE_COLOR
1359
1360
1361
		, FALSE
#endif
		);
1362
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1363

1364
1365
    free(nanorc);
    nanorc = NULL;
1366

1367
1368
1369
1370
1371
1372
1373
    if (errors && !ISSET(QUIET)) {
	errors = FALSE;
	fprintf(stderr,
		_("\nPress Enter to continue starting nano.\n"));
	while (getchar() != '\n')
	    ;
    }
1374
1375
}

1376
#endif /* !DISABLE_NANORC */