rcfile.c 33.9 KB
Newer Older
1
2
/* $Id$ */
/**************************************************************************
3
 *   rcfile.c                                                             *
4
 *                                                                        *
5
6
 *   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,  *
 *   2010, 2011, 2013, 2014 Free Software Foundation, Inc.                *
7
8
 *   This program is free software; you can redistribute it and/or modify *
 *   it under the terms of the GNU General Public License as published by *
9
 *   the Free Software Foundation; either version 3, or (at your option)  *
10
11
 *   any later version.                                                   *
 *                                                                        *
12
13
14
15
 *   This program is distributed in the hope that it will be useful, but  *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of           *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    *
 *   General Public License for more details.                             *
16
17
18
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
 *   along with this program; if not, write to the Free Software          *
19
20
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA            *
 *   02110-1301, USA.                                                     *
21
22
23
 *                                                                        *
 **************************************************************************/

24
#include "proto.h"
25

26
#include <glob.h>
27
#include <stdarg.h>
28
29
30
#include <string.h>
#include <stdio.h>
#include <errno.h>
Chris Allegretta's avatar
Chris Allegretta committed
31
#include <unistd.h>
32
#include <ctype.h>
33

34
#ifndef DISABLE_NANORC
35

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

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
125
126
127
/* We have an error in some part of the rcfile.  Print the error message
 * on stderr, and then make the user hit Enter to continue starting
 * nano. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
128
void rcfile_error(const char *msg, ...)
129
130
131
{
    va_list ap;

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

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

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

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
148
149
150
/* Parse the next word from the string, null-terminate it, and return
 * a pointer to the first character after the null terminator.  The
 * returned pointer will point to '\0' if we hit the end of the line. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
151
char *parse_next_word(char *ptr)
152
{
153
    while (!isblank(*ptr) && *ptr != '\0')
154
155
156
	ptr++;

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

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

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

    return ptr;
}
167

168
169
170
171
/* Parse an argument, with optional quotes, after a keyword that takes
 * one.  If the next word starts with a ", we say that it ends with the
 * last " of the line.  Otherwise, we interpret it as usual, so that the
 * arguments can contain "'s too. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
172
173
char *parse_argument(char *ptr)
{
174
    const char *ptr_save = ptr;
Chris Allegretta's avatar
Chris Allegretta committed
175
176
177
178
179
180
181
182
183
184
185
    char *last_quote = NULL;

    assert(ptr != NULL);

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

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

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

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

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

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

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

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

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

    return ptr;
}

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

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

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

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

254
255
/* Parse the next syntax string from the line at ptr, and add it to the
 * global list of color syntaxes. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
256
void parse_syntax(char *ptr)
257
{
Chris Allegretta's avatar
Chris Allegretta committed
258
    const char *fileregptr = NULL, *nameptr = NULL;
259
    syntaxtype *tmpsyntax, *prev_syntax;
260
    regexlisttype *endext = NULL;
261
	/* The end of the extensions list for this syntax. */
262

263
    assert(ptr != NULL);
264

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

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

276
277
278
279
280
    ptr++;

    nameptr = ptr;
    ptr = parse_next_regex(ptr);

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

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

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

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

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

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

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

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

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

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

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

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

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

357
358
359
360
	ptr++;

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

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

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

371
	    if (endext == NULL)
372
		endsyntax->extensions = newext;
373
374
375
376
	    else
		endext->next = newext;
	    endext = newext;
	    endext->next = NULL;
377
378
	} else
	    free(newext);
379
    }
380
}
381
#endif /* !DISABLE_COLOR */
382

383
384
385
386
387
388
389
390
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++)
391
	if (s->type == badtypes[i] && s->seq == badseqs[i])
392
393
394
395
396
	    return 1;

    return 0;
}

397
/* Bind or unbind a key combo, to or from a function. */
398
void parse_binding(char *ptr, bool dobind)
399
400
{
    char *keyptr = NULL, *keycopy = NULL, *funcptr = NULL, *menuptr = NULL;
401
    sc *s, *newsc = NULL;
402
    int menu;
403
404
405

    assert(ptr != NULL);

406
407
408
409
#ifdef DEBUG
    fprintf(stderr, "Starting the rebinding code...\n");
#endif

410
411
412
413
414
415
416
417
    if (*ptr == '\0') {
	rcfile_error(N_("Missing key name"));
	return;
    }

    keyptr = ptr;
    ptr = parse_next_word(ptr);
    keycopy = mallocstrcpy(NULL, keyptr);
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434

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

436
437
438
439
440
    /* 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') {
441
	rcfile_error(N_("Key name must begin with \"^\", \"M\", or \"F\""));
442
443
444
	return;
    }

445
446
447
    if (dobind) {
	funcptr = ptr;
	ptr = parse_next_word(ptr);
448

449
	if (!strcmp(funcptr, "")) {
450
	    rcfile_error(N_("Must specify a function to bind the key to"));
451
452
	    return;
	}
453
454
455
456
457
    }

    menuptr = ptr;
    ptr = parse_next_word(ptr);

458
    if (!strcmp(menuptr, "")) {
459
	/* TRANSLATORS: Do not translate the word "all". */
460
	rcfile_error(N_("Must specify a menu (or \"all\") in which to bind/unbind the key"));
461
462
	return;
    }
463
464

    if (dobind) {
465
	newsc = strtosc(funcptr);
466
	if (newsc == NULL) {
467
	    rcfile_error(N_("Cannot map name \"%s\" to a function"), funcptr);
468
469
	    return;
	}
470
471
    }

472
473
474
475
476
477
    menu = strtomenu(menuptr);
    if (menu < 1) {
	rcfile_error(N_("Cannot map name \"%s\" to a menu"), menuptr);
	return;
    }

478
#ifdef DEBUG
479
    if (dobind)
480
481
	fprintf(stderr, "newsc address is now %ld, assigned func = %ld, menu = %x\n",
	    (long)&newsc, (long)newsc->scfunc, menu);
482
    else
483
	fprintf(stderr, "unbinding \"%s\" from menu %x\n", keycopy, menu);
484
485
#endif

486
487
488
489
490
    if (dobind) {
	newsc->keystr = keycopy;
	newsc->menu = menu;
	newsc->type = strtokeytype(newsc->keystr);
	assign_keyinfo(newsc);
491
#ifdef DEBUG
492
493
	fprintf(stderr, "s->keystr = \"%s\"\n", newsc->keystr);
	fprintf(stderr, "s->seq = \"%d\"\n", newsc->seq);
494
495
#endif

496
	if (check_bad_binding(newsc)) {
497
498
	    rcfile_error(N_("Sorry, keystroke \"%s\" may not be rebound"), newsc->keystr);
	    free(newsc);
499
	    return;
500
501
	}
    }
502

503
    /* Now find and delete any existing same shortcut in the menu(s). */
504
    for (s = sclist; s != NULL; s = s->next) {
505
	if (((s->menu & menu)) && !strcmp(s->keystr, keycopy)) {
506
#ifdef DEBUG
507
	    fprintf(stderr, "deleting entry from menu %x\n", s->menu);
508
#endif
509
	    s->menu &= ~menu;
510
511
	}
    }
512
513
514
515
516
517

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

520

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

528
529
530
    /* 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). */
531
532

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

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

    /* 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. */
551
    nanorc = file;
552
553
    lineno = 0;

554
#ifdef DEBUG
555
    fprintf(stderr, "Parsing file \"%s\"\n", file);
556
557
#endif

558
    parse_rcfile(rcstream
559
#ifndef DISABLE_COLOR
560
561
562
	, TRUE
#endif
	);
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
}

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

587
588
589
    globfree(&files);
    free(expanded);

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

596
597
/* Return the short value corresponding to the color named in colorname,
 * and set bright to TRUE if that color is bright. */
598
short color_to_short(const char *colorname, bool *bright)
599
{
600
    short mcolor = -1;
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634

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

635
636
637
/* 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. */
638
void parse_colors(char *ptr, bool icase)
639
{
640
    short fg, bg;
641
    bool bright = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
642
    char *fgstr;
643

644
    assert(ptr != NULL);
645

646
647
    if (syntaxes == NULL) {
	rcfile_error(
648
		N_("Cannot add a color command without a syntax command"));
649
650
651
	return;
    }

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

657
658
    fgstr = ptr;
    ptr = parse_next_word(ptr);
659
660
    if (!parse_color_names(fgstr, &fg, &bg, &bright))
	return;
661

662
    if (*ptr == '\0') {
663
	rcfile_error(N_("Missing regex string"));
664
665
666
	return;
    }

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
667
    /* Now for the fun part.  Start adding regexes to individual strings
668
669
     * in the colorstrings array, woo! */
    while (ptr != NULL && *ptr != '\0') {
670
	colortype *newcolor;
671
	    /* The container for a color plus its regexes. */
672
	bool cancelled = FALSE;
673
	    /* The start expression was bad. */
674
675
	bool expectend = FALSE;
	    /* Do we expect an end= line? */
676

677
	if (strncasecmp(ptr, "start=", 6) == 0) {
678
	    ptr += 6;
679
	    expectend = TRUE;
680
681
682
	}

	if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
683
684
	    rcfile_error(
		N_("Regex strings must begin and end with a \" character"));
685
	    ptr = parse_next_regex(ptr);
686
687
	    continue;
	}
688

689
	ptr++;
690

691
692
	fgstr = ptr;
	ptr = parse_next_regex(ptr);
693
694
695
	if (ptr == NULL)
	    break;

696
	newcolor = (colortype *)nmalloc(sizeof(colortype));
697

698
699
700
	/* Save the starting regex string if it's valid, and set up the
	 * color information. */
	if (nregcomp(fgstr, icase ? REG_ICASE : 0)) {
701
702
703
	    newcolor->fg = fg;
	    newcolor->bg = bg;
	    newcolor->bright = bright;
704
	    newcolor->icase = icase;
705
706
707
708

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

709
	    newcolor->end_regex = NULL;
710
	    newcolor->end = NULL;
711

712
	    newcolor->next = NULL;
713

714
715
	    if (endcolor == NULL) {
		endsyntax->color = newcolor;
716
#ifdef DEBUG
717
		fprintf(stderr, "Starting a new colorstring for fg %hd, bg %hd\n", fg, bg);
718
#endif
719
	    } else {
Chris Allegretta's avatar
Chris Allegretta committed
720
#ifdef DEBUG
721
		fprintf(stderr, "Adding new entry for fg %hd, bg %hd\n", fg, bg);
Chris Allegretta's avatar
Chris Allegretta committed
722
#endif
723
724
		/* Need to recompute endcolor now so we can extend
		 * colors to syntaxes. */
725
		for (endcolor = endsyntax->color; endcolor->next != NULL; endcolor = endcolor->next)
726
		    ;
727
		endcolor->next = newcolor;
728
	    }
729

730
	    endcolor = newcolor;
731
732
733
	} else {
	    free(newcolor);
	    cancelled = TRUE;
734
	}
Chris Allegretta's avatar
Chris Allegretta committed
735

736
	if (expectend) {
737
	    if (ptr == NULL || strncasecmp(ptr, "end=", 4) != 0) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
738
739
		rcfile_error(
			N_("\"start=\" requires a corresponding \"end=\""));
740
741
742
743
		return;
	    }
	    ptr += 4;
	    if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
744
745
		rcfile_error(
			N_("Regex strings must begin and end with a \" character"));
746
		continue;
747
	    }
748

749
750
	    ptr++;

751
	    fgstr = ptr;
752
	    ptr = parse_next_regex(ptr);
753
754
	    if (ptr == NULL)
		break;
755

756
757
	    /* If the start regex was invalid, skip past the end regex
	     * to stay in sync. */
758
759
	    if (cancelled)
		continue;
760

761
762
763
	    /* Save the ending regex string if it's valid. */
	    newcolor->end_regex = (nregcomp(fgstr, icase ? REG_ICASE :
		0)) ? mallocstrcpy(NULL, fgstr) : NULL;
764

765
	    /* Lame way to skip another static counter. */
766
            newcolor->id = endsyntax->nmultis;
767
            endsyntax->nmultis++;
Chris Allegretta's avatar
Chris Allegretta committed
768
	}
769
    }
770
}
771

772
773
774
775
776
777
/* Parse the color name, or pair of color names, in combostr. */
bool parse_color_names(char *combostr, short *fg, short *bg, bool *bright)
{
    bool no_fgcolor = FALSE;

    if (combostr == NULL)
778
	return FALSE;
779
780
781
782
783
784
785
786
787
788
789
790
791

    if (strchr(combostr, ',') != NULL) {
	char *bgcolorname;
	strtok(combostr, ",");
	bgcolorname = strtok(NULL, ",");
	if (bgcolorname == NULL) {
	    /* If we have a background color without a foreground color,
	     * parse it properly. */
	    bgcolorname = combostr + 1;
	    no_fgcolor = TRUE;
	}
	if (strncasecmp(bgcolorname, "bright", 6) == 0) {
	    rcfile_error(N_("Background color \"%s\" cannot be bright"), bgcolorname);
792
	    return FALSE;
793
794
795
796
797
798
799
800
801
802
	}
	*bg = color_to_short(bgcolorname, bright);
    } else
	*bg = -1;

    if (!no_fgcolor) {
	*fg = color_to_short(combostr, bright);

	/* Don't try to parse screwed-up foreground colors. */
	if (*fg == -1)
803
	    return FALSE;
804
805
806
    } else
	*fg = -1;

807
    return TRUE;
808
809
}

810
/* Parse the header-line regexes that may influence the choice of syntax. */
811
void parse_header_exp(char *ptr)
812
{
813
    regexlisttype *endheader = NULL;
814
815
816
817
818
819
820
821
822
823
824
825
826
827

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

828
829
    while (*ptr != '\0') {
	const char *regexstring;
830
	regexlisttype *newheader;
831
832
833
834
835
836
837
838
839
840

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

	ptr++;

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

846
	newheader = (regexlisttype *)nmalloc(sizeof(regexlisttype));
847
848

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

853
	    if (endheader == NULL)
854
		endsyntax->headers = newheader;
855
	    else
856
857
		endheader->next = newheader;
	    endheader = newheader;
858
	    endheader->next = NULL;
859
860
861
862
	} else
	    free(newheader);
    }
}
863

864
865
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
923
924
925
926
927
928
929
930
931
#ifndef DISABLE_COLOR
/* Parse the magic regexes that may influence the choice of syntax. */
void parse_magic_exp(char *ptr)
{
#ifdef HAVE_LIBMAGIC
    regexlisttype *endmagic = 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 magic regexes into their part of the struct. */
    while (*ptr != '\0') {
	const char *regexstring;
	regexlisttype *newmagic;

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

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

	ptr++;

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

	newmagic = (regexlisttype *)nmalloc(sizeof(regexlisttype));

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

	    if (endmagic == NULL)
		endsyntax->magics = newmagic;
	    else
		endmagic->next = newmagic;
	    endmagic = newmagic;
	    endmagic->next = NULL;
	} else
	    free(newmagic);
    }
#endif /* HAVE_LIBMAGIC */
}
#endif /* !DISABLE_COLOR */

932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
/* 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);

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

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

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

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

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

	lineno++;
	ptr = buf;
1013
	while (isblank(*ptr))
1014
1015
	    ptr++;

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

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

1025
#ifndef DISABLE_COLOR
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
	/* 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);
	    }
	}
1046
#endif
1047

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

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

1109
1110
1111
1112
	if (set == 0)
	    continue;

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

	option = ptr;
	ptr = parse_next_word(ptr);

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

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

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

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

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

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

Chris Allegretta's avatar
Chris Allegretta committed
1288
    check_vitals_mapped();
1289
1290
1291
    return;
}

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

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

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

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

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

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

1331
    get_homedir();
1332

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

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

1366
1367
    free(nanorc);
    nanorc = NULL;
1368

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

1378
#endif /* !DISABLE_NANORC */