"src/global.c" did not exist on "cff6e6f60f284e5a7182be9fc49bb471818a5e1d"
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
#endif /* ENABLE_COLOR */
450

451

452
453
454
455
456
457
458
459
460
461
462
463
464
465
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;
}

466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
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]);

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

    funcptr = ptr;
    ptr = parse_next_word(ptr);

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

    menuptr = ptr;
    ptr = parse_next_word(ptr);

503
    if (!strcmp(menuptr, "")) {
504
	rcfile_error(
505
		/* TRANSLATORS: do not translate the word "all". */
506
		N_("Must specify menu to bind key to (or \"all\")"));
507
508
509
	return;
    }

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

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

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

529
530
531
532
533
534
535
536
537
    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

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

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

558
/* Let the user unbind a sequence from a given (or all) menus. */
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
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
579
    fprintf(stderr, "Starting the unbinding code...\n");
580
581
582
583
#endif

    if (keycopy[0] != 'M' && keycopy[0] != '^' && keycopy[0] != 'F' && keycopy[0] != 'K') {
	rcfile_error(
584
		N_("Keybindings must begin with \"^\", \"M\", or \"F\""));
585
586
587
588
589
590
591
592
	return;
    }

    menuptr = ptr;
    ptr = parse_next_word(ptr);

    if (!strcmp(menuptr, "")) {
	rcfile_error(
593
		/* TRANSLATORS: do not translate the word "all". */
594
595
596
597
598
599
600
601
602
603
604
605
		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
606
    fprintf(stderr, "unbinding \"%s\" from menu %x\n", keycopy, menu);
607
608
#endif

609
    /* Now find the appropriate entries in the menu to delete. */
610
611
612
    for (s = sclist; s != NULL; s = s->next) {
        if (((s->menu & menu)) && !strcmp(s->keystr,keycopy)) {
#ifdef DEBUG
613
	    fprintf(stderr, "deleting entry from menu %x\n", s->menu);
614
#endif
615
	    s->menu &= ~menu;
616
617
618
619
	}
    }
}

620

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

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

635
636
    /* 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) */
637
638

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

648
649
    expanded = real_dir_from_tilde(option);

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

    /* 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. */
659
    nanorc = expanded;
660
661
    lineno = 0;

662
#ifdef DEBUG
663
    fprintf(stderr, "Parsing file \"%s\" (expanded from \"%s\")\n", expanded, option);
664
665
#endif

666
667
668
669
670
671
672
673
674
675
676
677
678
    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;

}

679
680
/* Return the short value corresponding to the color named in colorname,
 * and set bright to TRUE if that color is bright. */
681
short color_to_short(const char *colorname, bool *bright)
682
{
683
    short mcolor = -1;
684
685
686
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

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

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

727
    assert(ptr != NULL);
728

729
730
    if (syntaxes == NULL) {
	rcfile_error(
731
		N_("Cannot add a color command without a syntax command"));
732
733
734
	return;
    }

735
    if (*ptr == '\0') {
736
	rcfile_error(N_("Missing color name"));
737
	return;
738
739
    }

740
741
742
743
    fgstr = ptr;
    ptr = parse_next_word(ptr);

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

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

764
    if (!no_fgcolor) {
765
	fg = color_to_short(fgstr, &bright);
766

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

773
    if (*ptr == '\0') {
774
	rcfile_error(N_("Missing regex string"));
775
776
777
	return;
    }

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

788
	if (strncasecmp(ptr, "start=", 6) == 0) {
789
	    ptr += 6;
790
	    expectend = TRUE;
791
792
793
	}

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

800
	ptr++;
801

802
803
	fgstr = ptr;
	ptr = parse_next_regex(ptr);
804
805
806
	if (ptr == NULL)
	    break;

807
	newcolor = (colortype *)nmalloc(sizeof(colortype));
808

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

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

820
	    newcolor->end_regex = NULL;
821
	    newcolor->end = NULL;
822

823
	    newcolor->next = NULL;
824

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

840
	    endcolor = newcolor;
841
842
843
	} else {
	    free(newcolor);
	    cancelled = TRUE;
844
	}
Chris Allegretta's avatar
Chris Allegretta committed
845

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

859
860
	    ptr++;

861
	    fgstr = ptr;
862
	    ptr = parse_next_regex(ptr);
863
864
	    if (ptr == NULL)
		break;
865
866
867
868
869

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

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

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

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

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

    }
}
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964


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

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

973
974


975
976
977
978
979
980
981
/* 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
982
    void (*vitals[VITALS])(void) = { do_exit, do_exit, do_cancel, do_cancel, do_cancel };
983
984
985
986
987
988
989
990
991
    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);
992
993
                   fprintf(stderr, _("Exiting.  If needed, use nano with the -I option "
				     "to adjust your nanorc settings.\n"));
994
995
996
997
998
999
1000
1001
                   exit(1);
               }
           break;
           }
       }
    }
}

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

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

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

	lineno++;
	ptr = buf;
1029
	while (isblank(*ptr))
1030
1031
	    ptr++;

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

1037
	/* Otherwise, skip to the next space. */
1038
1039
1040
	keyword = ptr;
	ptr = parse_next_word(ptr);

1041

1042
#ifdef ENABLE_COLOR
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
	/* 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);
	    }
	}
1063
#endif
1064

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

1117
1118
1119
1120
1121
1122
1123
1124
1125
#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

1126
1127
1128
1129
1130
	if (set == 0)
	    continue;

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

	option = ptr;
	ptr = parse_next_word(ptr);

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

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

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

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

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

Chris Allegretta's avatar
Chris Allegretta committed
1294
    check_vitals_mapped();
1295
1296
1297
    return;
}

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

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

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

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

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

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

1337
    get_homedir();
1338

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

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

1372
1373
    free(nanorc);
    nanorc = NULL;
1374

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

1383
1384
1385
#ifdef ENABLE_COLOR
    set_colorpairs();
#endif
1386
1387
}

1388
#endif /* ENABLE_NANORC */