rcfile.c 33.2 KB
Newer Older
1
2
/* $Id$ */
/**************************************************************************
3
 *   rcfile.c                                                             *
4
 *                                                                        *
5
 *   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009   *
6
 *   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
#endif
103
    {NULL, 0}
104
};
105

106
static bool errors = FALSE;
107
	/* Whether we got any errors while parsing an rcfile. */
108
static size_t lineno = 0;
109
	/* If we did, the line number where the last error occurred. */
110
static char *nanorc = NULL;
111
	/* The path to the rcfile we're parsing. */
112
#ifndef DISABLE_COLOR
113
114
static syntaxtype *endsyntax = NULL;
	/* The end of the list of syntaxes. */
115
static exttype *endheader = NULL;
116
	/* End of header list. */
117
118
119
static colortype *endcolor = NULL;
	/* The end of the color list for the current syntax. */
#endif
120

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

128
129
130
    if (ISSET(QUIET))
	return;

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

137
    va_start(ap, msg);
138
    vfprintf(stderr, _(msg), ap);
139
    va_end(ap);
140
141

    fprintf(stderr, "\n");
142
143
}

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

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

155
156
    /* Null-terminate and advance ptr. */
    *ptr++ = '\0';
157

158
    while (isblank(*ptr))
159
160
161
162
	ptr++;

    return ptr;
}
163

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

    assert(ptr != NULL);

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

    do {
	ptr++;
	if (*ptr == '"')
	    last_quote = ptr;
182
    } while (*ptr != '\0');
Chris Allegretta's avatar
Chris Allegretta committed
183
184
185
186
187
188

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

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

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

212
213
214
    assert(*ptr == '"' || *ptr == '\0');

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
220
    /* Null-terminate and advance ptr. */
221
222
    *ptr++ = '\0';

223
    while (isblank(*ptr))
224
225
226
227
228
	ptr++;

    return ptr;
}

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

    if (rc != 0) {
238
	size_t len = regerror(rc, &preg, NULL, 0);
239
240
	char *str = charalloc(len);

241
	regerror(rc, &preg, str, len);
242
	rcfile_error(N_("Bad regex \"%s\": %s"), r, str);
243
244
	free(str);
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
245

246
    regfree(&preg);
247
    return (rc == 0);
248
249
}

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

259
    assert(ptr != NULL);
260

261
262
    if (*ptr == '\0') {
	rcfile_error(N_("Missing syntax name"));
263
	return;
264
    }
265
266

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

272
273
274
275
276
    ptr++;

    nameptr = ptr;
    ptr = parse_next_regex(ptr);

277
    if (ptr == NULL)
278
	return;
279

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

	    if (endsyntax == tmpsyntax)
		endsyntax = prev_syntax;
290
291

	    tmpsyntax = tmpsyntax->next;
292
293
294
295
296
297
298
	    if (prev_syntax != NULL)
		prev_syntax->next = tmpsyntax;
	    else
		syntaxes = tmpsyntax;

	    free(old_syntax->desc);
	    free(old_syntax);
299
	    break;
300
	}
301
	prev_syntax = tmpsyntax;
302
303
    }

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

315
316
317
    endsyntax->desc = mallocstrcpy(NULL, nameptr);
    endsyntax->color = NULL;
    endcolor = NULL;
318
    endheader = NULL;
319
    endsyntax->extensions = NULL;
320
    endsyntax->headers = NULL;
321
    endsyntax->magics = NULL;
322
    endsyntax->next = NULL;
323
    endsyntax->nmultis = 0;
324
    endsyntax->linter = NULL;
325

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

330
331
332
333
334
335
336
    /* 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;
    }

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

344
345
    /* Now load the extensions into their part of the struct. */
    while (*ptr != '\0') {
346
347
348
	exttype *newext;
	    /* The new extension structure. */

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

352
	if (*ptr == '\0')
353
	    return;
354

355
356
357
358
	ptr++;

	fileregptr = ptr;
	ptr = parse_next_regex(ptr);
359
360
	if (ptr == NULL)
	    break;
361

362
	newext = (exttype *)nmalloc(sizeof(exttype));
363
364
365
366
367
368

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

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

380
/* Parse the magic regex that may influence the choice of syntax. */
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
void parse_magictype(char *ptr)
{
#ifdef HAVE_LIBMAGIC
    const char *fileregptr = NULL;
    exttype *endext = NULL;

    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

    /* Now load the extensions into their part of the struct. */
    while (*ptr != '\0') {
	exttype *newext;
	    /* The new extension structure. */

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

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

	ptr++;

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

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

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

	    if (endext == NULL)
		endsyntax->magics = newext;
	    else
		endext->next = newext;
	    endext = newext;
	    endext->next = NULL;
	} else
	    free(newext);
    }
#endif /* HAVE_LIBMAGIC */
445
}
446
#endif /* !DISABLE_COLOR */
447

448

449
450
451
452
453
454
455
456
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++)
457
	if (s->type == badtypes[i] && s->seq == badseqs[i])
458
459
460
461
462
	    return 1;

    return 0;
}

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

    assert(ptr != NULL);

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

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

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

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

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

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

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

    menuptr = ptr;
    ptr = parse_next_word(ptr);

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

530
    menu = strtomenu(menuptr);
531
532
533
534
    if (menu < 1) {
	rcfile_error(N_("Cannot map name \"%s\" to a menu"), menuptr);
	return;
    }
535
536

    if (dobind) {
537
	newsc = strtosc(funcptr);
538
	if (newsc == NULL) {
539
	    rcfile_error(N_("Cannot map name \"%s\" to a function"), funcptr);
540
541
	    return;
	}
542
543
    }

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

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

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

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

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

585

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

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

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

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

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

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

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

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));
    }
651
652
653
654
655
656
657

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

658
659
/* Return the short value corresponding to the color named in colorname,
 * and set bright to TRUE if that color is bright. */
660
short color_to_short(const char *colorname, bool *bright)
661
{
662
    short mcolor = -1;
663
664
665
666
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

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

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

706
    assert(ptr != NULL);
707

708
709
    if (syntaxes == NULL) {
	rcfile_error(
710
		N_("Cannot add a color command without a syntax command"));
711
712
713
	return;
    }

714
    if (*ptr == '\0') {
715
	rcfile_error(N_("Missing color name"));
716
	return;
717
718
    }

719
720
721
722
    fgstr = ptr;
    ptr = parse_next_word(ptr);

    if (strchr(fgstr, ',') != NULL) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
723
	char *bgcolorname;
724

725
	strtok(fgstr, ",");
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
726
	bgcolorname = strtok(NULL, ",");
727
728
729
730
731
732
	if (bgcolorname == NULL) {
	    /* If we have a background color without a foreground color,
	     * parse it properly. */
	    bgcolorname = fgstr + 1;
	    no_fgcolor = TRUE;
	}
733
	if (strncasecmp(bgcolorname, "bright", 6) == 0) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
734
	    rcfile_error(
735
		N_("Background color \"%s\" cannot be bright"),
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
736
		bgcolorname);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
737
738
	    return;
	}
739
	bg = color_to_short(bgcolorname, &bright);
740
    } else
Chris Allegretta's avatar
Chris Allegretta committed
741
	bg = -1;
742

743
    if (!no_fgcolor) {
744
	fg = color_to_short(fgstr, &bright);
745

746
747
748
749
750
	/* Don't try to parse screwed-up foreground colors. */
	if (fg == -1)
	    return;
    } else
	fg = -1;
751

752
    if (*ptr == '\0') {
753
	rcfile_error(N_("Missing regex string"));
754
755
756
	return;
    }

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
757
    /* Now for the fun part.  Start adding regexes to individual strings
758
759
     * in the colorstrings array, woo! */
    while (ptr != NULL && *ptr != '\0') {
760
761
	colortype *newcolor;
	    /* The new color structure. */
762
	bool cancelled = FALSE;
763
	    /* The start expression was bad. */
764
765
	bool expectend = FALSE;
	    /* Do we expect an end= line? */
766

767
	if (strncasecmp(ptr, "start=", 6) == 0) {
768
	    ptr += 6;
769
	    expectend = TRUE;
770
771
772
	}

	if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
773
774
	    rcfile_error(
		N_("Regex strings must begin and end with a \" character"));
775
	    ptr = parse_next_regex(ptr);
776
777
	    continue;
	}
778

779
	ptr++;
780

781
782
	fgstr = ptr;
	ptr = parse_next_regex(ptr);
783
784
785
	if (ptr == NULL)
	    break;

786
	newcolor = (colortype *)nmalloc(sizeof(colortype));
787

788
789
790
	/* Save the starting regex string if it's valid, and set up the
	 * color information. */
	if (nregcomp(fgstr, icase ? REG_ICASE : 0)) {
791
792
793
	    newcolor->fg = fg;
	    newcolor->bg = bg;
	    newcolor->bright = bright;
794
	    newcolor->icase = icase;
795
796
797
798

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

799
	    newcolor->end_regex = NULL;
800
	    newcolor->end = NULL;
801

802
	    newcolor->next = NULL;
803

804
805
	    if (endcolor == NULL) {
		endsyntax->color = newcolor;
806
#ifdef DEBUG
807
		fprintf(stderr, "Starting a new colorstring for fg %hd, bg %hd\n", fg, bg);
808
#endif
809
	    } else {
Chris Allegretta's avatar
Chris Allegretta committed
810
#ifdef DEBUG
811
		fprintf(stderr, "Adding new entry for fg %hd, bg %hd\n", fg, bg);
Chris Allegretta's avatar
Chris Allegretta committed
812
#endif
813
814
		/* Need to recompute endcolor now so we can extend
		 * colors to syntaxes. */
815
		for (endcolor = endsyntax->color; endcolor->next != NULL; endcolor = endcolor->next)
816
		    ;
817
		endcolor->next = newcolor;
818
	    }
819

820
	    endcolor = newcolor;
821
822
823
	} else {
	    free(newcolor);
	    cancelled = TRUE;
824
	}
Chris Allegretta's avatar
Chris Allegretta committed
825

826
	if (expectend) {
827
	    if (ptr == NULL || strncasecmp(ptr, "end=", 4) != 0) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
828
829
		rcfile_error(
			N_("\"start=\" requires a corresponding \"end=\""));
830
831
832
833
		return;
	    }
	    ptr += 4;
	    if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
834
835
		rcfile_error(
			N_("Regex strings must begin and end with a \" character"));
836
		continue;
837
	    }
838

839
840
	    ptr++;

841
	    fgstr = ptr;
842
	    ptr = parse_next_regex(ptr);
843
844
	    if (ptr == NULL)
		break;
845

846
847
	    /* If the start regex was invalid, skip past the end regex
	     * to stay in sync. */
848
849
	    if (cancelled)
		continue;
850

851
852
853
	    /* Save the ending regex string if it's valid. */
	    newcolor->end_regex = (nregcomp(fgstr, icase ? REG_ICASE :
		0)) ? mallocstrcpy(NULL, fgstr) : NULL;
854

855
	    /* Lame way to skip another static counter. */
856
            newcolor->id = endsyntax->nmultis;
857
            endsyntax->nmultis++;
Chris Allegretta's avatar
Chris Allegretta committed
858
	}
859
    }
860
}
861

862
/* Parse the header-line regex that may influence the choice of syntax. */
863
864
void parse_headers(char *ptr)
{
865
    char *regstr;
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
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
919
920
921
922

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

    /* Now for the fun part.  Start adding regexes to individual strings
     * in the colorstrings array, woo! */
    while (ptr != NULL && *ptr != '\0') {
	exttype *newheader;
	    /* The new color structure. */

	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;
	    newheader->next = NULL;

#ifdef DEBUG
	     fprintf(stderr, "Starting a new header entry: %s\n", newheader->ext_regex);
#endif

	    if (endheader == NULL) {
		endsyntax->headers = newheader;
	    } else {
		endheader->next = newheader;
	    }

	    endheader = newheader;
	} else
	    free(newheader);
    }
}
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942

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

943
944
945
946
947
    /* Let them unset the linter by using "" */
    if (!strcmp(ptr, "\"\""))
	endsyntax->linter = NULL;
    else
	endsyntax->linter = mallocstrcpy(syntaxes->linter, ptr);
948
}
949
#endif /* !DISABLE_COLOR */
950

951
/* Check whether the user has unmapped every shortcut for a
952
 * sequence we consider 'vital', like the exit function. */
953
954
955
956
957
static void check_vitals_mapped(void)
{
    subnfunc *f;
    int v;
#define VITALS 5
958
    void (*vitals[VITALS])(void) = { do_exit, do_exit, do_cancel, do_cancel, do_cancel };
959
960
961
962
963
964
965
    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) {
966
967
968
                   fprintf(stderr, _("Fatal error: no keys mapped for function "
				     "\"%s\".  Exiting.\n"), f->desc);
                   fprintf(stderr, _("If needed, use nano with the -I option "
969
				     "to adjust your nanorc settings.\n"));
970
971
972
973
974
975
976
977
                   exit(1);
               }
           break;
           }
       }
    }
}

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

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
999
	/* Ignore the newline. */
1000
1001
	if (buf[len - 1] == '\n')
	    buf[len - 1] = '\0';
1002
1003
1004

	lineno++;
	ptr = buf;
1005
	while (isblank(*ptr))
1006
1007
	    ptr++;

1008
1009
1010
	/* If we have a blank line or a comment, skip to the next
	 * line. */
	if (*ptr == '\0' || *ptr == '#')
1011
1012
	    continue;

1013
	/* Otherwise, skip to the next space. */
1014
1015
1016
	keyword = ptr;
	ptr = parse_next_word(ptr);

1017
#ifndef DISABLE_COLOR
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
	/* 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);
	    }
	}
1038
#endif
1039

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

1092
1093
1094
#ifndef DISABLE_COLOR
	/* If we temporarily reset endsyntax to allow extending,
	 * restore the value here. */
1095
1096
1097
1098
1099
1100
	if (end_syn_save != NULL) {
	    endsyntax = end_syn_save;
	    end_syn_save = NULL;
	}
#endif

1101
1102
1103
1104
	if (set == 0)
	    continue;

	if (*ptr == '\0') {
1105
	    rcfile_error(N_("Missing option"));
1106
1107
1108
1109
1110
1111
	    continue;
	}

	option = ptr;
	ptr = parse_next_word(ptr);

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

1136
			option = mallocstrcpy(NULL, option);
Chris Allegretta's avatar
Chris Allegretta committed
1137
#ifdef DEBUG
1138
			fprintf(stderr, "option = \"%s\"\n", option);
Chris Allegretta's avatar
Chris Allegretta committed
1139
#endif
1140
1141
1142
1143
1144
1145
1146
1147
1148

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

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

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

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

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

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

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

    /* Don't open directories, character files, or block files. */
    if (stat(nanorc, &rcinfo) != -1) {
	if (S_ISDIR(rcinfo.st_mode) || S_ISCHR(rcinfo.st_mode) ||
		S_ISBLK(rcinfo.st_mode))
	    rcfile_error(S_ISDIR(rcinfo.st_mode) ?
		_("\"%s\" is a directory") :
		_("\"%s\" is a device file"), nanorc);
    }

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

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

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

1312
    get_homedir();
1313

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

1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
	/* Don't open directories, character files, or block files. */
	if (stat(nanorc, &rcinfo) != -1) {
	    if (S_ISDIR(rcinfo.st_mode) || S_ISCHR(rcinfo.st_mode) ||
		S_ISBLK(rcinfo.st_mode))
		rcfile_error(S_ISDIR(rcinfo.st_mode) ?
			_("\"%s\" is a directory") :
			_("\"%s\" is a device file"), nanorc);
	}

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

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

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

1358
#ifndef DISABLE_COLOR
1359
1360
    set_colorpairs();
#endif
1361
1362
}

1363
#endif /* !DISABLE_NANORC */