rcfile.c 20.1 KB
Newer Older
1
2
/* $Id$ */
/**************************************************************************
3
 *   rcfile.c                                                             *
4
 *                                                                        *
5
 *   Copyright (C) 2001-2004 Chris Allegretta                             *
6
 *   Copyright (C) 2005-2006 David Lawrence Ramsey                        *
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 2, 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

35
const static 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
#ifndef DISABLE_MOUSE
45
    {"mouse", USE_MOUSE},
46
47
48
49
#endif
#ifdef ENABLE_MULTIBUFFER
    {"multibuffer", MULTIBUFFER},
#endif
50
    {"morespace", MORE_SPACE},
51
    {"nofollow", NOFOLLOW_SYMLINKS},
52
    {"nohelp", NO_HELP},
53
    {"nonewlines", NO_NEWLINES},
Chris Allegretta's avatar
Chris Allegretta committed
54
#ifndef DISABLE_WRAPPING
55
    {"nowrap", NO_WRAP},
Chris Allegretta's avatar
Chris Allegretta committed
56
#endif
57
#ifndef DISABLE_OPERATINGDIR
58
    {"operatingdir", 0},
59
#endif
Chris Allegretta's avatar
Chris Allegretta committed
60
    {"preserve", PRESERVE},
61
#ifndef DISABLE_JUSTIFY
62
    {"punct", 0},
63
64
    {"quotestr", 0},
#endif
65
    {"rebinddelete", REBIND_DELETE},
66
    {"rebindkeypad", REBIND_KEYPAD},
67
68
69
#ifdef HAVE_REGEX_H
    {"regexp", USE_REGEXP},
#endif
70
#ifndef DISABLE_SPELLER
71
    {"speller", 0},
72
73
74
#endif
    {"suspend", SUSPEND},
    {"tabsize", 0},
75
    {"tempfile", TEMP_FILE},
76
    {"view", VIEW_MODE},
77
#ifndef NANO_TINY
78
79
80
81
82
83
84
    {"autoindent", AUTOINDENT},
    {"backup", BACKUP_FILE},
    {"backupdir", 0},
    {"backwards", BACKWARDS_SEARCH},
    {"casesensitive", CASE_SENSITIVE},
    {"cut", CUT_TO_END},
    {"historylog", HISTORYLOG},
85
    {"matchbrackets", 0},
86
    {"noconvert", NO_CONVERT},
87
    {"quickblank", QUICK_BLANK},
88
89
90
    {"smarthome", SMART_HOME},
    {"smooth", SMOOTH_SCROLL},
    {"tabstospaces", TABS_TO_SPACES},
91
    {"whitespace", 0},
92
    {"wordbounds", WORD_BOUNDS},
93
#endif
94
    {NULL, 0}
95
};
96

97
static bool errors = FALSE;
98
	/* Whether we got any errors while parsing an rcfile. */
99
static size_t lineno = 0;
100
101
	/* If we did, the line number where the current error
	 * occurred. */
102
static char *nanorc = NULL;
103
	/* The path to the rcfile we're parsing. */
104
105
106
107
108
109
#ifdef ENABLE_COLOR
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
110

111
/* We have an error in some part of the rcfile.  Put it on stderr and
112
 * make the user hit Enter to continue starting up nano. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
113
void rcfile_error(const char *msg, ...)
114
115
116
117
{
    va_list ap;

    fprintf(stderr, "\n");
118
119
    if (lineno > 0) {
	errors = TRUE;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
120
	fprintf(stderr, _("Error in %s on line %lu: "), nanorc, (unsigned long)lineno);
121
    }
Chris Allegretta's avatar
Chris Allegretta committed
122

123
    va_start(ap, msg);
124
    vfprintf(stderr, _(msg), ap);
125
    va_end(ap);
126
127

    fprintf(stderr, "\n");
128
129
}

130
131
/* Parse the next word from the string.  Return points to '\0' if we hit
 * the end of the line. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
132
char *parse_next_word(char *ptr)
133
{
134
    while (!isblank(*ptr) && *ptr != '\0')
135
136
137
	ptr++;

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

140
141
    /* Null-terminate and advance ptr. */
    *ptr++ = '\0';
142

143
    while (isblank(*ptr))
144
145
146
147
	ptr++;

    return ptr;
}
148

149
150
151
152
/* 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
153
154
char *parse_argument(char *ptr)
{
155
    const char *ptr_save = ptr;
Chris Allegretta's avatar
Chris Allegretta committed
156
157
158
159
160
161
162
163
164
165
166
    char *last_quote = NULL;

    assert(ptr != NULL);

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

    do {
	ptr++;
	if (*ptr == '"')
	    last_quote = ptr;
167
    } while (*ptr != '\0');
Chris Allegretta's avatar
Chris Allegretta committed
168
169
170
171
172
173

    if (last_quote == NULL) {
	if (*ptr == '\0')
	    ptr = NULL;
	else
	    *ptr++ = '\0';
174
	rcfile_error(N_("Argument %s has unterminated \""), ptr_save);
Chris Allegretta's avatar
Chris Allegretta committed
175
176
177
178
179
    } else {
	*last_quote = '\0';
	ptr = last_quote + 1;
    }
    if (ptr != NULL)
180
	while (isblank(*ptr))
Chris Allegretta's avatar
Chris Allegretta committed
181
182
183
184
185
	    ptr++;
    return ptr;
}

#ifdef ENABLE_COLOR
186
187
/* Return the short value corresponding to the color named in colorname,
 * and set bright to TRUE if that color is bright. */
188
short color_to_short(const char *colorname, bool *bright)
189
{
190
    short mcolor = -1;
191

192
    assert(colorname != NULL && bright != NULL);
193

194
    if (strncasecmp(colorname, "bright", 6) == 0) {
195
	*bright = TRUE;
196
197
	colorname += 6;
    }
198

199
    if (strcasecmp(colorname, "green") == 0)
Chris Allegretta's avatar
Chris Allegretta committed
200
	mcolor = COLOR_GREEN;
201
    else if (strcasecmp(colorname, "red") == 0)
Chris Allegretta's avatar
Chris Allegretta committed
202
	mcolor = COLOR_RED;
203
    else if (strcasecmp(colorname, "blue") == 0)
Chris Allegretta's avatar
Chris Allegretta committed
204
	mcolor = COLOR_BLUE;
205
    else if (strcasecmp(colorname, "white") == 0)
Chris Allegretta's avatar
Chris Allegretta committed
206
	mcolor = COLOR_WHITE;
207
    else if (strcasecmp(colorname, "yellow") == 0)
Chris Allegretta's avatar
Chris Allegretta committed
208
	mcolor = COLOR_YELLOW;
209
    else if (strcasecmp(colorname, "cyan") == 0)
Chris Allegretta's avatar
Chris Allegretta committed
210
	mcolor = COLOR_CYAN;
211
    else if (strcasecmp(colorname, "magenta") == 0)
Chris Allegretta's avatar
Chris Allegretta committed
212
	mcolor = COLOR_MAGENTA;
213
    else if (strcasecmp(colorname, "black") == 0)
Chris Allegretta's avatar
Chris Allegretta committed
214
	mcolor = COLOR_BLACK;
215
    else
216
	rcfile_error(N_("Color %s not understood.\n"
217
218
219
		"Valid colors are \"green\", \"red\", \"blue\",\n"
		"\"white\", \"yellow\", \"cyan\", \"magenta\" and\n"
		"\"black\", with the optional prefix \"bright\"\n"
220
		"for foreground colors."), colorname);
221

222
223
224
    return mcolor;
}

225
/* Parse the next regex string from the line at ptr, and return it. */
226
227
char *parse_next_regex(char *ptr)
{
228
229
230
231
    assert(ptr != NULL);

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

236
237
238
    assert(*ptr == '"' || *ptr == '\0');

    if (*ptr == '\0') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
239
240
	rcfile_error(
		N_("Regex strings must begin and end with a \" character"));
241
	return NULL;
242
    }
243

244
    /* Null terminate and advance ptr. */
245
246
    *ptr++ = '\0';

247
    while (isblank(*ptr))
248
249
250
251
252
	ptr++;

    return ptr;
}

253
254
255
/* Compile the regular expression regex to see if it's valid.  Return
 * TRUE if it is, or FALSE otherwise. */
bool nregcomp(const char *regex, int eflags)
256
{
257
258
    regex_t preg;
    int rc = regcomp(&preg, regex, REG_EXTENDED | eflags);
259
260

    if (rc != 0) {
261
	size_t len = regerror(rc, &preg, NULL, 0);
262
263
	char *str = charalloc(len);

264
	regerror(rc, &preg, str, len);
265
	rcfile_error(N_("Bad regex \"%s\": %s"), regex, str);
266
267
	free(str);
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
268

269
270
    regfree(&preg);

271
    return (rc == 0);
272
273
}

274
275
/* 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
276
void parse_syntax(char *ptr)
277
{
Chris Allegretta's avatar
Chris Allegretta committed
278
    const char *fileregptr = NULL, *nameptr = NULL;
279
    const syntaxtype *tmpsyntax;
280
281
    exttype *endext = NULL;
	/* The end of the extensions list for this syntax. */
282

283
    assert(ptr != NULL);
284

285
286
    if (*ptr == '\0') {
	rcfile_error(N_("Missing syntax name"));
287
	return;
288
    }
289
290

    if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
291
292
	rcfile_error(
		N_("Regex strings must begin and end with a \" character"));
293
	return;
294
    }
295

296
297
298
299
300
    ptr++;

    nameptr = ptr;
    ptr = parse_next_regex(ptr);

301
    if (ptr == NULL)
302
	return;
303

304
305
    for (tmpsyntax = syntaxes; tmpsyntax != NULL;
	tmpsyntax = tmpsyntax->next) {
306
	if (strcmp(nameptr, tmpsyntax->desc) == 0) {
307
308
309
310
311
	    rcfile_error(N_("Duplicate syntax name %s"), nameptr);
	    return;
	}
    }

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

323
324
325
326
327
    endsyntax->desc = mallocstrcpy(NULL, nameptr);
    endsyntax->color = NULL;
    endcolor = NULL;
    endsyntax->extensions = NULL;
    endsyntax->next = 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
348
    /* Now load the extensions into their part of the struct. */
    while (*ptr != '\0') {
349
350
351
	exttype *newext;
	    /* The new extension structure. */

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

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

358
359
360
361
	ptr++;

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

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

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

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

383
384
385
/* 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. */
386
void parse_colors(char *ptr, bool icase)
387
{
388
    short fg, bg;
389
    bool bright = FALSE, no_fgcolor = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
390
    char *fgstr;
391

392
    assert(ptr != NULL);
393

394
    if (*ptr == '\0') {
395
	rcfile_error(N_("Missing color name"));
396
	return;
397
398
    }

399
400
401
402
    fgstr = ptr;
    ptr = parse_next_word(ptr);

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

405
	strtok(fgstr, ",");
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
406
	bgcolorname = strtok(NULL, ",");
407
408
409
410
411
412
	if (bgcolorname == NULL) {
	    /* If we have a background color without a foreground color,
	     * parse it properly. */
	    bgcolorname = fgstr + 1;
	    no_fgcolor = TRUE;
	}
413
	if (strncasecmp(bgcolorname, "bright", 6) == 0) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
414
415
416
	    rcfile_error(
		N_("Background color %s cannot be bright"),
		bgcolorname);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
417
418
	    return;
	}
419
	bg = color_to_short(bgcolorname, &bright);
420
    } else
Chris Allegretta's avatar
Chris Allegretta committed
421
	bg = -1;
422

423
    if (!no_fgcolor) {
424
	fg = color_to_short(fgstr, &bright);
425

426
427
428
429
430
	/* Don't try to parse screwed-up foreground colors. */
	if (fg == -1)
	    return;
    } else
	fg = -1;
431

432
    if (syntaxes == NULL) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
433
434
	rcfile_error(
		N_("Cannot add a color directive without a syntax line"));
435
	return;
436
437
    }

438
    if (*ptr == '\0') {
439
	rcfile_error(N_("Missing regex string"));
440
441
442
	return;
    }

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
443
    /* Now for the fun part.  Start adding regexes to individual strings
444
445
     * in the colorstrings array, woo! */
    while (ptr != NULL && *ptr != '\0') {
446
447
	colortype *newcolor;
	    /* The new color structure. */
448
	bool cancelled = FALSE;
449
	    /* The start expression was bad. */
450
451
	bool expectend = FALSE;
	    /* Do we expect an end= line? */
452

453
	if (strncasecmp(ptr, "start=", 6) == 0) {
454
	    ptr += 6;
455
	    expectend = TRUE;
456
457
458
	}

	if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
459
460
	    rcfile_error(
		N_("Regex strings must begin and end with a \" character"));
461
	    ptr = parse_next_regex(ptr);
462
463
	    continue;
	}
464

465
	ptr++;
466

467
468
	fgstr = ptr;
	ptr = parse_next_regex(ptr);
469
470
471
	if (ptr == NULL)
	    break;

472
	newcolor = (colortype *)nmalloc(sizeof(colortype));
473

474
475
476
	/* Save the starting regex string if it's valid, and set up the
	 * color information. */
	if (nregcomp(fgstr, icase ? REG_ICASE : 0)) {
477
478
479
	    newcolor->fg = fg;
	    newcolor->bg = bg;
	    newcolor->bright = bright;
480
	    newcolor->icase = icase;
481
482
483
484

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

485
	    newcolor->end_regex = NULL;
486
	    newcolor->end = NULL;
487

488
	    newcolor->next = NULL;
489

490
491
	    if (endcolor == NULL) {
		endsyntax->color = newcolor;
492
#ifdef DEBUG
493
		fprintf(stderr, "Starting a new colorstring for fg %hd, bg %hd\n", fg, bg);
494
#endif
495
	    } else {
Chris Allegretta's avatar
Chris Allegretta committed
496
#ifdef DEBUG
497
		fprintf(stderr, "Adding new entry for fg %hd, bg %hd\n", fg, bg);
Chris Allegretta's avatar
Chris Allegretta committed
498
#endif
499
		endcolor->next = newcolor;
500
	    }
501

502
	    endcolor = newcolor;
503
504
505
	} else {
	    free(newcolor);
	    cancelled = TRUE;
506
	}
Chris Allegretta's avatar
Chris Allegretta committed
507

508
	if (expectend) {
509
	    if (ptr == NULL || strncasecmp(ptr, "end=", 4) != 0) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
510
511
		rcfile_error(
			N_("\"start=\" requires a corresponding \"end=\""));
512
513
514
515
		return;
	    }
	    ptr += 4;
	    if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
516
517
		rcfile_error(
			N_("Regex strings must begin and end with a \" character"));
518
		continue;
519
	    }
520

521
522
	    ptr++;

523
	    fgstr = ptr;
524
	    ptr = parse_next_regex(ptr);
525
526
	    if (ptr == NULL)
		break;
527
528
529
530
531

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

533
534
535
	    /* Save the ending regex string if it's valid. */
	    newcolor->end_regex = (nregcomp(fgstr, icase ? REG_ICASE :
		0)) ? mallocstrcpy(NULL, fgstr) : NULL;
Chris Allegretta's avatar
Chris Allegretta committed
536
	}
537
    }
538
}
539
#endif /* ENABLE_COLOR */
540

541
542
/* Parse the rcfile, once it has been opened successfully at
 * rcstream. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
543
void parse_rcfile(FILE *rcstream)
544
{
545
546
547
548
549
550
551
552
    char *buf = NULL;
    ssize_t len;
    size_t n;

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
553
	/* Ignore the newline. */
554
	buf[len - 1] = '\0';
555
556
557

	lineno++;
	ptr = buf;
558
	while (isblank(*ptr))
559
560
	    ptr++;

561
562
563
	/* If we have a blank line or a comment, skip to the next
	 * line. */
	if (*ptr == '\0' || *ptr == '#')
564
565
	    continue;

566
	/* Otherwise, skip to the next space. */
567
568
569
	keyword = ptr;
	ptr = parse_next_word(ptr);

570
	/* Try to parse the keyword. */
571
	if (strcasecmp(keyword, "set") == 0)
572
	    set = 1;
573
	else if (strcasecmp(keyword, "unset") == 0)
574
	    set = -1;
575
#ifdef ENABLE_COLOR
576
	else if (strcasecmp(keyword, "syntax") == 0)
Chris Allegretta's avatar
Chris Allegretta committed
577
	    parse_syntax(ptr);
578
	else if (strcasecmp(keyword, "color") == 0)
579
580
581
	    parse_colors(ptr, FALSE);
	else if (strcasecmp(keyword, "icolor") == 0)
	    parse_colors(ptr, TRUE);
582
583
#endif /* ENABLE_COLOR */
	else
584
	    rcfile_error(N_("Command %s not understood"), keyword);
585
586
587
588
589
590

	if (set == 0)
	    continue;

	if (*ptr == '\0') {
	    rcfile_error(N_("Missing flag"));
591
592
593
594
595
596
	    continue;
	}

	option = ptr;
	ptr = parse_next_word(ptr);

597
598
	for (i = 0; rcopts[i].name != NULL; i++) {
	    if (strcasecmp(option, rcopts[i].name) == 0) {
599
#ifdef DEBUG
600
601
602
603
604
605
606
607
608
609
610
		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
			 *an argument. */
			if (*ptr == '\0') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
611
612
613
			    rcfile_error(
				N_("Option %s requires an argument"),
				rcopts[i].name);
614
615
616
617
618
619
			    break;
			}
			option = ptr;
			if (*option == '"')
			    option++;
			ptr = parse_argument(ptr);
620

621
			option = mallocstrcpy(NULL, option);
Chris Allegretta's avatar
Chris Allegretta committed
622
#ifdef DEBUG
623
			fprintf(stderr, "option = \"%s\"\n", option);
Chris Allegretta's avatar
Chris Allegretta committed
624
#endif
625
626
627
628
629
630
631
632
633

			/* 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
634
#ifndef DISABLE_OPERATINGDIR
635
			if (strcasecmp(rcopts[i].name, "operatingdir") == 0)
636
			    operating_dir = option;
637
			else
Chris Allegretta's avatar
Chris Allegretta committed
638
#endif
639
#ifndef DISABLE_WRAPJUSTIFY
640
641
			if (strcasecmp(rcopts[i].name, "fill") == 0) {
			    if (!parse_num(option, &wrap_at)) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
642
643
644
				rcfile_error(
					N_("Requested fill size %s invalid"),
					option);
645
				wrap_at = -CHARS_FROM_EOL;
646
647
			    } else
				free(option);
648
			} else
Chris Allegretta's avatar
Chris Allegretta committed
649
#endif
650
#ifndef NANO_TINY
651
652
653
654
655
656
657
658
659
660
661
			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) {
662
			    whitespace = option;
663
664
			    if (mbstrlen(whitespace) != 2 ||
				strlenpt(whitespace) != 2) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
665
666
				rcfile_error(
					N_("Two single-column characters required"));
667
668
				free(whitespace);
				whitespace = NULL;
669
670
671
			    } else {
				whitespace_len[0] =
					parse_mbchar(whitespace, NULL,
672
					NULL);
673
674
				whitespace_len[1] =
					parse_mbchar(whitespace +
675
					whitespace_len[0], NULL, NULL);
676
677
			    }
			} else
678
#endif
679
#ifndef DISABLE_JUSTIFY
680
			if (strcasecmp(rcopts[i].name, "punct") == 0) {
681
			    punct = option;
682
			    if (has_blank_mbchars(punct)) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
683
				rcfile_error(
684
					N_("Non-blank characters required"));
685
686
687
				free(punct);
				punct = NULL;
			    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
688
689
			} else if (strcasecmp(rcopts[i].name,
				"brackets") == 0) {
690
			    brackets = option;
691
			    if (has_blank_mbchars(brackets)) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
692
				rcfile_error(
693
					N_("Non-blank characters required"));
694
695
696
				free(brackets);
				brackets = NULL;
			    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
697
698
			} else if (strcasecmp(rcopts[i].name,
				"quotestr") == 0)
699
			    quotestr = option;
700
			else
701
#endif
702
#ifndef NANO_TINY
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
703
704
			if (strcasecmp(rcopts[i].name,
				"backupdir") == 0)
705
			    backup_dir = option;
706
			else
707
#endif
708
#ifndef DISABLE_SPELLER
709
			if (strcasecmp(rcopts[i].name, "speller") == 0)
710
			    alt_speller = option;
711
712
			else
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
713
714
715
716
717
718
719
			if (strcasecmp(rcopts[i].name,
				"tabsize") == 0) {
			    if (!parse_num(option, &tabsize) ||
				tabsize <= 0) {
				rcfile_error(
					N_("Requested tab size %s invalid"),
					option);
720
				tabsize = -1;
721
722
			    } else
				free(option);
723
			} else
724
725
			    assert(FALSE);
		    }
726
#ifdef DEBUG
727
		    fprintf(stderr, "flag = %ld\n", rcopts[i].flag);
728
#endif
729
730
731
		} else if (rcopts[i].flag != 0)
		    UNSET(rcopts[i].flag);
		else
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
732
733
		    rcfile_error(N_("Cannot unset flag %s"),
			rcopts[i].name);
734
		break;
735
736
	    }
	}
737
738
	if (rcopts[i].name == NULL)
	    rcfile_error(N_("Unknown flag %s"), option);
739
    }
740

Chris Allegretta's avatar
Chris Allegretta committed
741
    free(buf);
742
743
    fclose(rcstream);
    lineno = 0;
744
745
746

    if (errors) {
	errors = FALSE;
747
	fprintf(stderr, _("\nPress Enter to continue starting nano\n"));
748
749
750
751
	while (getchar() != '\n')
	    ;
    }

752
753
754
    return;
}

755
/* The main rcfile function.  It tries to open the system-wide rcfile,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
756
 * followed by the current user's rcfile. */
757
758
void do_rcfile(void)
{
759
    struct stat rcinfo;
760
761
    FILE *rcstream;

762
    nanorc = mallocstrcpy(nanorc, SYSCONFDIR "/nanorc");
763
764
765
766
767
768
769
770
771
772

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

773
    /* Try to open the system-wide nanorc. */
774
    rcstream = fopen(nanorc, "rb");
775
    if (rcstream != NULL)
Chris Allegretta's avatar
Chris Allegretta committed
776
	parse_rcfile(rcstream);
777

778
779
780
781
782
783
784
#if defined(DISABLE_ROOTWRAP) && !defined(DISABLE_WRAPPING)
    /* We've already read SYSCONFDIR/nanorc, if it's there.  If we're
     * root and --disable-wrapping-as-root is used, turn wrapping
     * off now. */
    if (geteuid() == NANO_ROOT_UID)
	SET(NO_WRAP);
#endif
Chris Allegretta's avatar
Chris Allegretta committed
785

786
    get_homedir();
787

788
    if (homedir == NULL)
789
	rcfile_error(N_("I can't find my home directory!  Wah!"));
790
791
792
793
    else {
	nanorc = charealloc(nanorc, strlen(homedir) + 9);
	sprintf(nanorc, "%s/.nanorc", homedir);

794
795
796
797
798
799
800
801
802
803
804
	/* 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");
805
	if (rcstream == NULL) {
806
807
	    /* Don't complain about the file's not existing. */
	    if (errno != ENOENT)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
808
809
		rcfile_error(N_("Error reading %s: %s"), nanorc,
			strerror(errno));
810
	} else
Chris Allegretta's avatar
Chris Allegretta committed
811
	    parse_rcfile(rcstream);
812
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
813

814
815
    free(nanorc);
    nanorc = NULL;
816

817
818
819
#ifdef ENABLE_COLOR
    set_colorpairs();
#endif
820
821
}

822
#endif /* ENABLE_NANORC */