rcfile.c 33.5 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 <stdarg.h>
27
28
29
#include <string.h>
#include <stdio.h>
#include <errno.h>
Chris Allegretta's avatar
Chris Allegretta committed
30
#include <unistd.h>
31
#include <ctype.h>
32
33
34

#ifdef ENABLE_NANORC

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

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

119
#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
200
	    ptr++;
    return ptr;
}

#ifdef ENABLE_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
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
445
446
447

}


/* Parse the next syntax string from the line at ptr, and add it to the
 * global list of color syntaxes. */
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 */
448
449
}

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

    return 0;
}

464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
void parse_keybinding(char *ptr)
{
    char *keyptr = NULL, *keycopy = NULL, *funcptr = NULL, *menuptr = NULL;
    sc *s, *newsc;
    int i, menu;

    assert(ptr != NULL);

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

    keyptr = ptr;
    ptr = parse_next_word(ptr);
    keycopy = mallocstrcpy(NULL, keyptr);
    for (i = 0; i < strlen(keycopy); i++)
	keycopy[i] = toupper(keycopy[i]);

483
    if (keycopy[0] != 'M' && keycopy[0] != '^' && keycopy[0] != 'F' && keycopy[0] != 'K') {
484
	rcfile_error(
485
		N_("keybindings must begin with \"^\", \"M\", or \"F\""));
486
487
488
489
490
491
	return;
    }

    funcptr = ptr;
    ptr = parse_next_word(ptr);

492
    if (!strcmp(funcptr, "")) {
493
	rcfile_error(
494
		N_("Must specify function to bind key to"));
495
496
497
498
499
500
	return;
    }

    menuptr = ptr;
    ptr = parse_next_word(ptr);

501
    if (!strcmp(menuptr, "")) {
502
503
504
	rcfile_error(
		/* Note to translators, do not translate the word "all"
		   in the sentence below, everything else is fine */
505
		N_("Must specify menu to bind key to (or \"all\")"));
506
507
508
	return;
    }

509
    menu = strtomenu(menuptr);
510
511
    newsc = strtosc(menu, funcptr);
    if (newsc == NULL) {
512
	rcfile_error(
513
		N_("Could not map name \"%s\" to a function"), funcptr);
514
515
516
	return;
    }

517
    if (menu < 1) {
518
	rcfile_error(
519
		N_("Could not map name \"%s\" to a menu"), menuptr);
520
521
	return;
    }
522
523


524
525
#ifdef DEBUG
    fprintf(stderr, "newsc now address %d, menu func assigned = %d, menu = %d\n",
526
	&newsc, newsc->scfunc, menu);
527
528
529
#endif


530
531
532
533
534
535
536
537
538
    newsc->keystr = keycopy;
    newsc->menu = menu;
    newsc->type = strtokeytype(newsc->keystr);
    assign_keyinfo(newsc);
#ifdef DEBUG
    fprintf(stderr, "s->keystr = \"%s\"\n", newsc->keystr);
    fprintf(stderr, "s->seq = \"%d\"\n", newsc->seq);
#endif

539
540
    if (check_bad_binding(newsc)) {
	rcfile_error(
541
		N_("Sorry, keystr \"%s\" is an illegal binding"), newsc->keystr);
542
543
544
	return;
    }

545
546
547
548
    /* now let's have some fun.  Try and delete the other entries
       we found for the same menu, then make this new new
       beginning */
    for (s = sclist; s != NULL; s = s->next) {
Chris Allegretta's avatar
Chris Allegretta committed
549
        if (((s->menu & newsc->menu)) && s->seq == newsc->seq) {
550
551
552
553
554
555
556
557
558
559
	    s->menu &= ~newsc->menu;
#ifdef DEBUG
	    fprintf(stderr, "replaced menu entry %d\n", s->menu);
#endif
	}
    }
    newsc->next = sclist;
    sclist = newsc;
}

560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
/* Let user unbind a sequence from a given (or all) menus */
void parse_unbinding(char *ptr)
{
    char *keyptr = NULL, *keycopy = NULL, *menuptr = NULL;
    sc *s;
    int i, menu;

    assert(ptr != NULL);

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

    keyptr = ptr;
    ptr = parse_next_word(ptr);
    keycopy = mallocstrcpy(NULL, keyptr);
    for (i = 0; i < strlen(keycopy); i++)
	keycopy[i] = toupper(keycopy[i]);

#ifdef DEBUG
    fprintf(stderr, "Starting unbinding code");
#endif

    if (keycopy[0] != 'M' && keycopy[0] != '^' && keycopy[0] != 'F' && keycopy[0] != 'K') {
	rcfile_error(
		N_("keybindings must begin with \"^\", \"M\", or \"F\""));
	return;
    }

    menuptr = ptr;
    ptr = parse_next_word(ptr);

    if (!strcmp(menuptr, "")) {
	rcfile_error(
		/* Note to translators, do not translate the word "all"
		   in the sentence below, everything else is fine */
		N_("Must specify menu to bind key to (or \"all\")"));
	return;
    }

    menu = strtomenu(menuptr);
    if (menu < 1) {
	rcfile_error(
		N_("Could not map name \"%s\" to a menu"), menuptr);
	return;
    }


#ifdef DEBUG
    fprintf(stderr, "unbinding \"%s\" from menu = %d\n", keycopy, menu);
#endif

    /* Now find the apropriate entries in the menu to delete */
    for (s = sclist; s != NULL; s = s->next) {
        if (((s->menu & menu)) && !strcmp(s->keystr,keycopy)) {
	    s->menu &= ~menu;
#ifdef DEBUG
	    fprintf(stderr, "deleted menu entry %d\n", s->menu);
#endif
	}
    }
}

624

625
626
627
628
629
/* Read and parse additional syntax files. */
void parse_include(char *ptr)
{
    struct stat rcinfo;
    FILE *rcstream;
630
    char *option, *nanorc_save = nanorc, *expanded;
631
632
633
634
635
636
637
    size_t lineno_save = lineno;

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

638
639
    /* Can't get the specified file's full path cause it may screw up
	our cwd depending on the parent dirs' permissions, (see Savannah bug 25297) */
640
641

    /* Don't open directories, character files, or block files. */
642
    if (stat(option, &rcinfo) != -1) {
643
644
645
646
	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") :
647
		_("\"%s\" is a device file"), option);
648
649
650
	}
    }

651
652
    expanded = real_dir_from_tilde(option);

653
    /* Open the new syntax file. */
654
655
    if ((rcstream = fopen(expanded, "rb")) == NULL) {
	rcfile_error(_("Error reading %s: %s"), expanded,
656
		strerror(errno));
657
	return;
658
659
660
661
    }

    /* 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. */
662
    nanorc = expanded;
663
664
    lineno = 0;

665
#ifdef DEBUG
666
    fprintf(stderr, "Parsing file \"%s\" (expanded from \"%s\")\n", expanded, option);
667
668
#endif

669
670
671
672
673
674
675
676
677
678
679
680
681
    parse_rcfile(rcstream
#ifdef ENABLE_COLOR
	, TRUE
#endif
	);

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

}

682
683
/* Return the short value corresponding to the color named in colorname,
 * and set bright to TRUE if that color is bright. */
684
short color_to_short(const char *colorname, bool *bright)
685
{
686
    short mcolor = -1;
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720

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

721
722
723
/* 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. */
724
void parse_colors(char *ptr, bool icase)
725
{
726
    short fg, bg;
727
    bool bright = FALSE, no_fgcolor = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
728
    char *fgstr;
729

730
    assert(ptr != NULL);
731

732
733
    if (syntaxes == NULL) {
	rcfile_error(
734
		N_("Cannot add a color command without a syntax command"));
735
736
737
	return;
    }

738
    if (*ptr == '\0') {
739
	rcfile_error(N_("Missing color name"));
740
	return;
741
742
    }

743
744
745
746
    fgstr = ptr;
    ptr = parse_next_word(ptr);

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

749
	strtok(fgstr, ",");
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
750
	bgcolorname = strtok(NULL, ",");
751
752
753
754
755
756
	if (bgcolorname == NULL) {
	    /* If we have a background color without a foreground color,
	     * parse it properly. */
	    bgcolorname = fgstr + 1;
	    no_fgcolor = TRUE;
	}
757
	if (strncasecmp(bgcolorname, "bright", 6) == 0) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
758
	    rcfile_error(
759
		N_("Background color \"%s\" cannot be bright"),
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
760
		bgcolorname);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
761
762
	    return;
	}
763
	bg = color_to_short(bgcolorname, &bright);
764
    } else
Chris Allegretta's avatar
Chris Allegretta committed
765
	bg = -1;
766

767
    if (!no_fgcolor) {
768
	fg = color_to_short(fgstr, &bright);
769

770
771
772
773
774
	/* Don't try to parse screwed-up foreground colors. */
	if (fg == -1)
	    return;
    } else
	fg = -1;
775

776
    if (*ptr == '\0') {
777
	rcfile_error(N_("Missing regex string"));
778
779
780
	return;
    }

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
781
    /* Now for the fun part.  Start adding regexes to individual strings
782
783
     * in the colorstrings array, woo! */
    while (ptr != NULL && *ptr != '\0') {
784
785
	colortype *newcolor;
	    /* The new color structure. */
786
	bool cancelled = FALSE;
787
	    /* The start expression was bad. */
788
789
	bool expectend = FALSE;
	    /* Do we expect an end= line? */
790

791
	if (strncasecmp(ptr, "start=", 6) == 0) {
792
	    ptr += 6;
793
	    expectend = TRUE;
794
795
796
	}

	if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
797
798
	    rcfile_error(
		N_("Regex strings must begin and end with a \" character"));
799
	    ptr = parse_next_regex(ptr);
800
801
	    continue;
	}
802

803
	ptr++;
804

805
806
	fgstr = ptr;
	ptr = parse_next_regex(ptr);
807
808
809
	if (ptr == NULL)
	    break;

810
	newcolor = (colortype *)nmalloc(sizeof(colortype));
811

812
813
814
	/* Save the starting regex string if it's valid, and set up the
	 * color information. */
	if (nregcomp(fgstr, icase ? REG_ICASE : 0)) {
815
816
817
	    newcolor->fg = fg;
	    newcolor->bg = bg;
	    newcolor->bright = bright;
818
	    newcolor->icase = icase;
819
820
821
822

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

823
	    newcolor->end_regex = NULL;
824
	    newcolor->end = NULL;
825

826
	    newcolor->next = NULL;
827

828
829
	    if (endcolor == NULL) {
		endsyntax->color = newcolor;
830
#ifdef DEBUG
831
		fprintf(stderr, "Starting a new colorstring for fg %hd, bg %hd\n", fg, bg);
832
#endif
833
	    } else {
Chris Allegretta's avatar
Chris Allegretta committed
834
#ifdef DEBUG
835
		fprintf(stderr, "Adding new entry for fg %hd, bg %hd\n", fg, bg);
Chris Allegretta's avatar
Chris Allegretta committed
836
#endif
837
838
839
		/* Need to recompute endcolor now so we can extend colors to syntaxes */
		for (endcolor = endsyntax->color; endcolor->next != NULL; endcolor = endcolor->next)
			;
840
		endcolor->next = newcolor;
841
	    }
842

843
	    endcolor = newcolor;
844
845
846
	} else {
	    free(newcolor);
	    cancelled = TRUE;
847
	}
Chris Allegretta's avatar
Chris Allegretta committed
848

849
	if (expectend) {
850
	    if (ptr == NULL || strncasecmp(ptr, "end=", 4) != 0) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
851
852
		rcfile_error(
			N_("\"start=\" requires a corresponding \"end=\""));
853
854
855
856
		return;
	    }
	    ptr += 4;
	    if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
857
858
		rcfile_error(
			N_("Regex strings must begin and end with a \" character"));
859
		continue;
860
	    }
861

862
863
	    ptr++;

864
	    fgstr = ptr;
865
	    ptr = parse_next_regex(ptr);
866
867
	    if (ptr == NULL)
		break;
868
869
870
871
872

	    /* If the start regex was invalid, skip past the end regex to
	     * stay in sync. */
	    if (cancelled)
		continue;
873

874
875
876
	    /* Save the ending regex string if it's valid. */
	    newcolor->end_regex = (nregcomp(fgstr, icase ? REG_ICASE :
		0)) ? mallocstrcpy(NULL, fgstr) : NULL;
877
878

	    /* Lame way to skip another static counter */
879
            newcolor->id = endsyntax->nmultis;
880
            endsyntax->nmultis++;
Chris Allegretta's avatar
Chris Allegretta committed
881
	}
882
    }
883
}
884
885
886
887

/* Parse the headers (1st line) of the file which may influence the regex used. */
void parse_headers(char *ptr)
{
888
    char *regstr;
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
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946

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

    }
}
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967


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

968
969
970
971
972
    /* Let them unset the linter by using "" */
    if (!strcmp(ptr, "\"\""))
	endsyntax->linter = NULL;
    else
	endsyntax->linter = mallocstrcpy(syntaxes->linter, ptr);
973
}
974
#endif /* ENABLE_COLOR */
975

976
977


978
979
980
981
982
983
984
/* Check whether the user has unmapped every shortcut for a
sequence we consider 'vital', like the exit function */
static void check_vitals_mapped(void)
{
    subnfunc *f;
    int v;
#define VITALS 5
985
    void (*vitals[VITALS])(void) = { do_exit, do_exit, do_cancel, do_cancel, do_cancel };
986
987
988
989
990
991
992
993
994
    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) {
                   rcfile_error(N_("Fatal error: no keys mapped for function \"%s\""),
                       f->desc);
995
996
                   fprintf(stderr, _("Exiting.  If needed, use nano with the -I option "
				     "to adjust your nanorc settings.\n"));
997
998
999
1000
1001
1002
1003
1004
                   exit(1);
               }
           break;
           }
       }
    }
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1005
1006
1007
/* 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. */
1008
1009
1010
1011
1012
void parse_rcfile(FILE *rcstream
#ifdef ENABLE_COLOR
	, bool syntax_only
#endif
	)
1013
{
1014
1015
    char *buf = NULL;
    ssize_t len;
1016
    size_t n = 0;
1017
1018
1019
#ifdef ENABLE_COLOR
    syntaxtype *end_syn_save = NULL;
#endif
1020
1021
1022

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1026
	/* Ignore the newline. */
1027
1028
	if (buf[len - 1] == '\n')
	    buf[len - 1] = '\0';
1029
1030
1031

	lineno++;
	ptr = buf;
1032
	while (isblank(*ptr))
1033
1034
	    ptr++;

1035
1036
1037
	/* If we have a blank line or a comment, skip to the next
	 * line. */
	if (*ptr == '\0' || *ptr == '#')
1038
1039
	    continue;

1040
	/* Otherwise, skip to the next space. */
1041
1042
1043
	keyword = ptr;
	ptr = parse_next_word(ptr);

1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065

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

1066
	/* Try to parse the keyword. */
1067
	if (strcasecmp(keyword, "set") == 0) {
1068
#ifdef ENABLE_COLOR
1069
1070
	    if (syntax_only)
		rcfile_error(
1071
			N_("Command \"%s\" not allowed in included file"),
1072
1073
1074
1075
1076
1077
1078
1079
			keyword);
	    else
#endif
		set = 1;
	} else if (strcasecmp(keyword, "unset") == 0) {
#ifdef ENABLE_COLOR
	    if (syntax_only)
		rcfile_error(
1080
			N_("Command \"%s\" not allowed in included file"),
1081
1082
1083
1084
1085
1086
1087
1088
1089
			keyword);
	    else
#endif
		set = -1;
	}
#ifdef ENABLE_COLOR
	else if (strcasecmp(keyword, "include") == 0) {
	    if (syntax_only)
		rcfile_error(
1090
			N_("Command \"%s\" not allowed in included file"),
1091
1092
1093
			keyword);
	    else
		parse_include(ptr);
1094
1095
	} else if (strcasecmp(keyword, "syntax") == 0) {
	    if (endsyntax != NULL && endcolor == NULL)
1096
		rcfile_error(N_("Syntax \"%s\" has no color commands"),
1097
			endsyntax->desc);
Chris Allegretta's avatar
Chris Allegretta committed
1098
	    parse_syntax(ptr);
1099
1100
	}
	else if (strcasecmp(keyword, "magic") == 0) {
1101
	    parse_magictype(ptr);
1102
1103
1104
	} else if (strcasecmp(keyword, "header") == 0)
	    parse_headers(ptr);
	else if (strcasecmp(keyword, "color") == 0)
1105
1106
1107
	    parse_colors(ptr, FALSE);
	else if (strcasecmp(keyword, "icolor") == 0)
	    parse_colors(ptr, TRUE);
1108
1109
	else if (strcasecmp(keyword, "bind") == 0)
	    parse_keybinding(ptr);
1110
1111
	else if (strcasecmp(keyword, "unbind") == 0)
	    parse_unbinding(ptr);
1112
1113
1114
	else if (strcasecmp(keyword, "linter") == 0) {
	    parse_linter(ptr);
	}
1115
1116
#endif /* ENABLE_COLOR */
	else
1117
	    rcfile_error(N_("Command \"%s\" not understood"), keyword);
1118

1119
1120
1121
1122
1123
1124
1125
1126
1127
#ifdef ENABLE_COLOR
	/* If we temporarily reset emdsyntax to allow extending, reset
	   the value here */
	if (end_syn_save != NULL) {
	    endsyntax = end_syn_save;
	    end_syn_save = NULL;
	}
#endif

1128
1129
1130
1131
1132
	if (set == 0)
	    continue;

	if (*ptr == '\0') {
	    rcfile_error(N_("Missing flag"));
1133
1134
1135
1136
1137
1138
	    continue;
	}

	option = ptr;
	ptr = parse_next_word(ptr);

1139
1140
	for (i = 0; rcopts[i].name != NULL; i++) {
	    if (strcasecmp(option, rcopts[i].name) == 0) {
1141
#ifdef DEBUG
1142
1143
1144
1145
1146
1147
1148
1149
1150
		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
1151
			 * an argument. */
1152
			if (*ptr == '\0') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1153
			    rcfile_error(
1154
				N_("Option \"%s\" requires an argument"),
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1155
				rcopts[i].name);
1156
1157
1158
1159
1160
1161
			    break;
			}
			option = ptr;
			if (*option == '"')
			    option++;
			ptr = parse_argument(ptr);
1162

1163
			option = mallocstrcpy(NULL, option);
Chris Allegretta's avatar
Chris Allegretta committed
1164
#ifdef DEBUG
1165
			fprintf(stderr, "option = \"%s\"\n", option);
Chris Allegretta's avatar
Chris Allegretta committed
1166
#endif
1167
1168
1169
1170
1171
1172
1173
1174
1175

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

1286
#ifdef ENABLE_COLOR
1287
    if (endsyntax != NULL && endcolor == NULL)
1288
	rcfile_error(N_("Syntax \"%s\" has no color commands"),
1289
		endsyntax->desc);
1290
#endif
1291

Chris Allegretta's avatar
Chris Allegretta committed
1292
    free(buf);
1293
1294
    fclose(rcstream);
    lineno = 0;
1295

Chris Allegretta's avatar
Chris Allegretta committed
1296
    check_vitals_mapped();
1297
1298
1299
    return;
}

1300
/* The main rcfile function.  It tries to open the system-wide rcfile,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1301
 * followed by the current user's rcfile. */
1302
1303
void do_rcfile(void)
{
1304
    struct stat rcinfo;
1305
1306
    FILE *rcstream;

1307
    nanorc = mallocstrcpy(nanorc, SYSCONFDIR "/nanorc");
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317

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

1318
1319
1320
1321
#ifdef DEBUG
    fprintf(stderr, "Parsing file \"%s\"\n", nanorc);
#endif

1322
    /* Try to open the system-wide nanorc. */
1323
    rcstream = fopen(nanorc, "rb");
1324
    if (rcstream != NULL)
1325
1326
1327
1328
1329
	parse_rcfile(rcstream
#ifdef ENABLE_COLOR
		, FALSE
#endif
		);
1330

1331
#ifdef DISABLE_ROOTWRAPPING
1332
    /* We've already read SYSCONFDIR/nanorc, if it's there.  If we're
1333
1334
     * root, and --disable-wrapping-as-root is used, turn wrapping off
     * now. */
1335
1336
1337
    if (geteuid() == NANO_ROOT_UID)
	SET(NO_WRAP);
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1338

1339
    get_homedir();
1340

1341
    if (homedir == NULL)
1342
	rcfile_error(N_("I can't find my home directory!  Wah!"));
1343
    else {
1344
1345
1346
1347
1348
#ifndef RCFILE_NAME
#define RCFILE_NAME ".nanorc"
#endif
	nanorc = charealloc(nanorc, strlen(homedir) + strlen(RCFILE_NAME) + 2);
	sprintf(nanorc, "%s/%s", homedir, RCFILE_NAME);
1349

1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
	/* 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");
1361
	if (rcstream == NULL) {
1362
1363
	    /* Don't complain about the file's not existing. */
	    if (errno != ENOENT)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1364
1365
		rcfile_error(N_("Error reading %s: %s"), nanorc,
			strerror(errno));
1366
	} else
1367
1368
1369
1370
1371
	    parse_rcfile(rcstream
#ifdef ENABLE_COLOR
		, FALSE
#endif
		);
1372
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1373

1374
1375
    free(nanorc);
    nanorc = NULL;
1376

1377
1378
1379
1380
1381
1382
1383
1384
    if (errors && !ISSET(QUIET)) {
	errors = FALSE;
	fprintf(stderr,
		_("\nPress Enter to continue starting nano.\n"));
	while (getchar() != '\n')
	    ;
    }

1385
1386
1387
#ifdef ENABLE_COLOR
    set_colorpairs();
#endif
1388
1389
}

1390
#endif /* ENABLE_NANORC */