rcfile.c 22.8 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 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 an 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
/* Parse the next regex string from the line at ptr, and return it. */
187
188
char *parse_next_regex(char *ptr)
{
189
190
191
192
    assert(ptr != NULL);

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

197
198
199
    assert(*ptr == '"' || *ptr == '\0');

    if (*ptr == '\0') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
200
201
	rcfile_error(
		N_("Regex strings must begin and end with a \" character"));
202
	return NULL;
203
    }
204

205
    /* Null terminate and advance ptr. */
206
207
    *ptr++ = '\0';

208
    while (isblank(*ptr))
209
210
211
212
213
	ptr++;

    return ptr;
}

214
215
216
/* 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)
217
{
218
219
    regex_t preg;
    int rc = regcomp(&preg, regex, REG_EXTENDED | eflags);
220
221

    if (rc != 0) {
222
	size_t len = regerror(rc, &preg, NULL, 0);
223
224
	char *str = charalloc(len);

225
	regerror(rc, &preg, str, len);
226
	rcfile_error(N_("Bad regex \"%s\": %s"), regex, str);
227
228
	free(str);
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
229

230
231
    regfree(&preg);

232
    return (rc == 0);
233
234
}

235
236
/* 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
237
void parse_syntax(char *ptr)
238
{
Chris Allegretta's avatar
Chris Allegretta committed
239
    const char *fileregptr = NULL, *nameptr = NULL;
240
    syntaxtype *tmpsyntax;
241
242
    exttype *endext = NULL;
	/* The end of the extensions list for this syntax. */
243

244
    assert(ptr != NULL);
245

246
247
    if (*ptr == '\0') {
	rcfile_error(N_("Missing syntax name"));
248
	return;
249
    }
250
251

    if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
252
253
	rcfile_error(
		N_("Regex strings must begin and end with a \" character"));
254
	return;
255
    }
256

257
258
259
260
261
    ptr++;

    nameptr = ptr;
    ptr = parse_next_regex(ptr);

262
    if (ptr == NULL)
263
	return;
264

265
266
    /* Search for a duplicate syntax name.  If we find one, free it, so
     * that we always use the last syntax with a given name. */
267
268
    for (tmpsyntax = syntaxes; tmpsyntax != NULL;
	tmpsyntax = tmpsyntax->next) {
269
	if (strcmp(nameptr, tmpsyntax->desc) == 0) {
270
271
272
273
274
	    syntaxtype *prev_syntax = tmpsyntax;

	    tmpsyntax = tmpsyntax->next;
	    free(prev_syntax);
	    break;
275
276
277
	}
    }

Chris Allegretta's avatar
Chris Allegretta committed
278
279
    if (syntaxes == NULL) {
	syntaxes = (syntaxtype *)nmalloc(sizeof(syntaxtype));
280
	endsyntax = syntaxes;
Chris Allegretta's avatar
Chris Allegretta committed
281
    } else {
282
283
	endsyntax->next = (syntaxtype *)nmalloc(sizeof(syntaxtype));
	endsyntax = endsyntax->next;
284
#ifdef DEBUG
285
	fprintf(stderr, "Adding new syntax after first one\n");
286
#endif
Chris Allegretta's avatar
Chris Allegretta committed
287
    }
288

289
290
291
292
293
    endsyntax->desc = mallocstrcpy(NULL, nameptr);
    endsyntax->color = NULL;
    endcolor = NULL;
    endsyntax->extensions = NULL;
    endsyntax->next = NULL;
294

295
#ifdef DEBUG
296
    fprintf(stderr, "Starting a new syntax type: \"%s\"\n", nameptr);
297
298
#endif

299
300
301
302
303
304
305
    /* 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;
    }

306
    /* The default syntax should have no associated extensions. */
307
    if (strcmp(endsyntax->desc, "default") == 0 && *ptr != '\0') {
308
309
	rcfile_error(
		N_("The \"default\" syntax must take no extensions"));
310
311
312
	return;
    }

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

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

321
	if (*ptr == '\0')
322
	    return;
323

324
325
326
327
	ptr++;

	fileregptr = ptr;
	ptr = parse_next_regex(ptr);
328
329
	if (ptr == NULL)
	    break;
330

331
	newext = (exttype *)nmalloc(sizeof(exttype));
332
333
334
335
336
337

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

338
	    if (endext == NULL)
339
		endsyntax->extensions = newext;
340
341
342
343
	    else
		endext->next = newext;
	    endext = newext;
	    endext->next = NULL;
344
345
	} else
	    free(newext);
346
    }
347
348
}

349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
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
/* Read and parse additional syntax files. */
void parse_include(char *ptr)
{
    struct stat rcinfo;
    FILE *rcstream;
    char *option, *full_option, *nanorc_save = nanorc;
    size_t lineno_save = lineno;

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

    /* Get the specified file's full path. */
    full_option = get_full_path(option);

    if (full_option == NULL) {
	rcfile_error(_("Error reading %s: %s"), option, strerror(errno));
	goto cleanup_include;
    }

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

    /* Open the new syntax file. */
    if ((rcstream = fopen(full_option, "rb")) == NULL) {
	rcfile_error(_("Error reading %s: %s"), full_option,
		strerror(errno));
	goto cleanup_include;
    }

    /* 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. */
    nanorc = full_option;
    lineno = 0;

    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;

  cleanup_include:
    free(full_option);
}

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

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

447
448
449
/* 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. */
450
void parse_colors(char *ptr, bool icase)
451
{
452
    short fg, bg;
453
    bool bright = FALSE, no_fgcolor = FALSE;
Chris Allegretta's avatar
Chris Allegretta committed
454
    char *fgstr;
455

456
    assert(ptr != NULL);
457

458
459
    if (syntaxes == NULL) {
	rcfile_error(
460
		N_("Cannot add a color command without a syntax command"));
461
462
463
	return;
    }

464
    if (*ptr == '\0') {
465
	rcfile_error(N_("Missing color name"));
466
	return;
467
468
    }

469
470
471
472
    fgstr = ptr;
    ptr = parse_next_word(ptr);

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

475
	strtok(fgstr, ",");
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
476
	bgcolorname = strtok(NULL, ",");
477
478
479
480
481
482
	if (bgcolorname == NULL) {
	    /* If we have a background color without a foreground color,
	     * parse it properly. */
	    bgcolorname = fgstr + 1;
	    no_fgcolor = TRUE;
	}
483
	if (strncasecmp(bgcolorname, "bright", 6) == 0) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
484
	    rcfile_error(
485
		N_("Background color \"%s\" cannot be bright"),
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
486
		bgcolorname);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
487
488
	    return;
	}
489
	bg = color_to_short(bgcolorname, &bright);
490
    } else
Chris Allegretta's avatar
Chris Allegretta committed
491
	bg = -1;
492

493
    if (!no_fgcolor) {
494
	fg = color_to_short(fgstr, &bright);
495

496
497
498
499
500
	/* Don't try to parse screwed-up foreground colors. */
	if (fg == -1)
	    return;
    } else
	fg = -1;
501

502
    if (*ptr == '\0') {
503
	rcfile_error(N_("Missing regex string"));
504
505
506
	return;
    }

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
507
    /* Now for the fun part.  Start adding regexes to individual strings
508
509
     * in the colorstrings array, woo! */
    while (ptr != NULL && *ptr != '\0') {
510
511
	colortype *newcolor;
	    /* The new color structure. */
512
	bool cancelled = FALSE;
513
	    /* The start expression was bad. */
514
515
	bool expectend = FALSE;
	    /* Do we expect an end= line? */
516

517
	if (strncasecmp(ptr, "start=", 6) == 0) {
518
	    ptr += 6;
519
	    expectend = TRUE;
520
521
522
	}

	if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
523
524
	    rcfile_error(
		N_("Regex strings must begin and end with a \" character"));
525
	    ptr = parse_next_regex(ptr);
526
527
	    continue;
	}
528

529
	ptr++;
530

531
532
	fgstr = ptr;
	ptr = parse_next_regex(ptr);
533
534
535
	if (ptr == NULL)
	    break;

536
	newcolor = (colortype *)nmalloc(sizeof(colortype));
537

538
539
540
	/* Save the starting regex string if it's valid, and set up the
	 * color information. */
	if (nregcomp(fgstr, icase ? REG_ICASE : 0)) {
541
542
543
	    newcolor->fg = fg;
	    newcolor->bg = bg;
	    newcolor->bright = bright;
544
	    newcolor->icase = icase;
545
546
547
548

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

549
	    newcolor->end_regex = NULL;
550
	    newcolor->end = NULL;
551

552
	    newcolor->next = NULL;
553

554
555
	    if (endcolor == NULL) {
		endsyntax->color = newcolor;
556
#ifdef DEBUG
557
		fprintf(stderr, "Starting a new colorstring for fg %hd, bg %hd\n", fg, bg);
558
#endif
559
	    } else {
Chris Allegretta's avatar
Chris Allegretta committed
560
#ifdef DEBUG
561
		fprintf(stderr, "Adding new entry for fg %hd, bg %hd\n", fg, bg);
Chris Allegretta's avatar
Chris Allegretta committed
562
#endif
563
		endcolor->next = newcolor;
564
	    }
565

566
	    endcolor = newcolor;
567
568
569
	} else {
	    free(newcolor);
	    cancelled = TRUE;
570
	}
Chris Allegretta's avatar
Chris Allegretta committed
571

572
	if (expectend) {
573
	    if (ptr == NULL || strncasecmp(ptr, "end=", 4) != 0) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
574
575
		rcfile_error(
			N_("\"start=\" requires a corresponding \"end=\""));
576
577
578
579
		return;
	    }
	    ptr += 4;
	    if (*ptr != '"') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
580
581
		rcfile_error(
			N_("Regex strings must begin and end with a \" character"));
582
		continue;
583
	    }
584

585
586
	    ptr++;

587
	    fgstr = ptr;
588
	    ptr = parse_next_regex(ptr);
589
590
	    if (ptr == NULL)
		break;
591
592
593
594
595

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

597
598
599
	    /* 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
600
	}
601
    }
602
}
603
#endif /* ENABLE_COLOR */
604

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
605
606
607
/* 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. */
608
609
610
611
612
void parse_rcfile(FILE *rcstream
#ifdef ENABLE_COLOR
	, bool syntax_only
#endif
	)
613
{
614
615
616
617
618
619
    char *buf = NULL;
    ssize_t len;
    size_t n;

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

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
623
	/* Ignore the newline. */
624
625
	if (buf[len - 1] == '\n')
	    buf[len - 1] = '\0';
626
627
628

	lineno++;
	ptr = buf;
629
	while (isblank(*ptr))
630
631
	    ptr++;

632
633
634
	/* If we have a blank line or a comment, skip to the next
	 * line. */
	if (*ptr == '\0' || *ptr == '#')
635
636
	    continue;

637
	/* Otherwise, skip to the next space. */
638
639
640
	keyword = ptr;
	ptr = parse_next_word(ptr);

641
	/* Try to parse the keyword. */
642
	if (strcasecmp(keyword, "set") == 0) {
643
#ifdef ENABLE_COLOR
644
645
	    if (syntax_only)
		rcfile_error(
646
			N_("Command \"%s\" not allowed in included file"),
647
648
649
650
651
652
653
654
			keyword);
	    else
#endif
		set = 1;
	} else if (strcasecmp(keyword, "unset") == 0) {
#ifdef ENABLE_COLOR
	    if (syntax_only)
		rcfile_error(
655
			N_("Command \"%s\" not allowed in included file"),
656
657
658
659
660
661
662
663
664
			keyword);
	    else
#endif
		set = -1;
	}
#ifdef ENABLE_COLOR
	else if (strcasecmp(keyword, "include") == 0) {
	    if (syntax_only)
		rcfile_error(
665
			N_("Command \"%s\" not allowed in included file"),
666
667
668
			keyword);
	    else
		parse_include(ptr);
669
670
	} else if (strcasecmp(keyword, "syntax") == 0) {
	    if (endsyntax != NULL && endcolor == NULL)
671
		rcfile_error(N_("Syntax \"%s\" has no color commands"),
672
			endsyntax->desc);
Chris Allegretta's avatar
Chris Allegretta committed
673
	    parse_syntax(ptr);
674
	} else if (strcasecmp(keyword, "color") == 0)
675
676
677
	    parse_colors(ptr, FALSE);
	else if (strcasecmp(keyword, "icolor") == 0)
	    parse_colors(ptr, TRUE);
678
679
#endif /* ENABLE_COLOR */
	else
680
	    rcfile_error(N_("Command \"%s\" not understood"), keyword);
681
682
683
684
685
686

	if (set == 0)
	    continue;

	if (*ptr == '\0') {
	    rcfile_error(N_("Missing flag"));
687
688
689
690
691
692
	    continue;
	}

	option = ptr;
	ptr = parse_next_word(ptr);

693
694
	for (i = 0; rcopts[i].name != NULL; i++) {
	    if (strcasecmp(option, rcopts[i].name) == 0) {
695
#ifdef DEBUG
696
697
698
699
700
701
702
703
704
		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
705
			 * an argument. */
706
			if (*ptr == '\0') {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
707
			    rcfile_error(
708
				N_("Option \"%s\" requires an argument"),
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
709
				rcopts[i].name);
710
711
712
713
714
715
			    break;
			}
			option = ptr;
			if (*option == '"')
			    option++;
			ptr = parse_argument(ptr);
716

717
			option = mallocstrcpy(NULL, option);
Chris Allegretta's avatar
Chris Allegretta committed
718
#ifdef DEBUG
719
			fprintf(stderr, "option = \"%s\"\n", option);
Chris Allegretta's avatar
Chris Allegretta committed
720
#endif
721
722
723
724
725
726
727
728
729

			/* 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
730
#ifndef DISABLE_OPERATINGDIR
731
			if (strcasecmp(rcopts[i].name, "operatingdir") == 0)
732
			    operating_dir = option;
733
			else
Chris Allegretta's avatar
Chris Allegretta committed
734
#endif
735
#ifndef DISABLE_WRAPJUSTIFY
736
737
			if (strcasecmp(rcopts[i].name, "fill") == 0) {
			    if (!parse_num(option, &wrap_at)) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
738
				rcfile_error(
739
					N_("Requested fill size \"%s\" is invalid"),
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
740
					option);
741
				wrap_at = -CHARS_FROM_EOL;
742
743
			    } else
				free(option);
744
			} else
Chris Allegretta's avatar
Chris Allegretta committed
745
#endif
746
#ifndef NANO_TINY
747
748
749
750
751
752
753
754
755
756
757
			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) {
758
			    whitespace = option;
759
760
			    if (mbstrlen(whitespace) != 2 ||
				strlenpt(whitespace) != 2) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
761
762
				rcfile_error(
					N_("Two single-column characters required"));
763
764
				free(whitespace);
				whitespace = NULL;
765
766
767
			    } else {
				whitespace_len[0] =
					parse_mbchar(whitespace, NULL,
768
					NULL);
769
770
				whitespace_len[1] =
					parse_mbchar(whitespace +
771
					whitespace_len[0], NULL, NULL);
772
773
			    }
			} else
774
#endif
775
#ifndef DISABLE_JUSTIFY
776
			if (strcasecmp(rcopts[i].name, "punct") == 0) {
777
			    punct = option;
778
			    if (has_blank_mbchars(punct)) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
779
				rcfile_error(
780
					N_("Non-blank characters required"));
781
782
783
				free(punct);
				punct = NULL;
			    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
784
785
			} else if (strcasecmp(rcopts[i].name,
				"brackets") == 0) {
786
			    brackets = option;
787
			    if (has_blank_mbchars(brackets)) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
788
				rcfile_error(
789
					N_("Non-blank characters required"));
790
791
792
				free(brackets);
				brackets = NULL;
			    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
793
794
			} else if (strcasecmp(rcopts[i].name,
				"quotestr") == 0)
795
			    quotestr = option;
796
			else
797
#endif
798
#ifndef NANO_TINY
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
799
800
			if (strcasecmp(rcopts[i].name,
				"backupdir") == 0)
801
			    backup_dir = option;
802
			else
803
#endif
804
#ifndef DISABLE_SPELLER
805
			if (strcasecmp(rcopts[i].name, "speller") == 0)
806
			    alt_speller = option;
807
808
			else
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
809
810
811
812
813
			if (strcasecmp(rcopts[i].name,
				"tabsize") == 0) {
			    if (!parse_num(option, &tabsize) ||
				tabsize <= 0) {
				rcfile_error(
814
					N_("Requested tab size \"%s\" is invalid"),
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
815
					option);
816
				tabsize = -1;
817
818
			    } else
				free(option);
819
			} else
820
821
			    assert(FALSE);
		    }
822
#ifdef DEBUG
823
		    fprintf(stderr, "flag = %ld\n", rcopts[i].flag);
824
#endif
825
826
827
		} else if (rcopts[i].flag != 0)
		    UNSET(rcopts[i].flag);
		else
828
		    rcfile_error(N_("Cannot unset flag \"%s\""),
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
829
			rcopts[i].name);
830
		break;
831
832
	    }
	}
833
	if (rcopts[i].name == NULL)
834
	    rcfile_error(N_("Unknown flag \"%s\""), option);
835
    }
836

837
    if (endsyntax != NULL && endcolor == NULL)
838
	rcfile_error(N_("Syntax \"%s\" has no color commands"),
839
		endsyntax->desc);
840

Chris Allegretta's avatar
Chris Allegretta committed
841
    free(buf);
842
843
    fclose(rcstream);
    lineno = 0;
844
845
846

    if (errors) {
	errors = FALSE;
847
848
	fprintf(stderr,
		_("\nPress Enter to continue starting nano.\n"));
849
850
851
852
	while (getchar() != '\n')
	    ;
    }

853
854
855
    return;
}

856
/* The main rcfile function.  It tries to open the system-wide rcfile,
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
857
 * followed by the current user's rcfile. */
858
859
void do_rcfile(void)
{
860
    struct stat rcinfo;
861
862
    FILE *rcstream;

863
    nanorc = mallocstrcpy(nanorc, SYSCONFDIR "/nanorc");
864
865
866
867
868
869
870
871
872
873

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

874
    /* Try to open the system-wide nanorc. */
875
    rcstream = fopen(nanorc, "rb");
876
    if (rcstream != NULL)
877
878
879
880
881
	parse_rcfile(rcstream
#ifdef ENABLE_COLOR
		, FALSE
#endif
		);
882

883
#ifdef DISABLE_ROOTWRAP
884
    /* We've already read SYSCONFDIR/nanorc, if it's there.  If we're
885
886
     * root, and --disable-wrapping-as-root is used, turn wrapping off
     * now. */
887
888
889
    if (geteuid() == NANO_ROOT_UID)
	SET(NO_WRAP);
#endif
Chris Allegretta's avatar
Chris Allegretta committed
890

891
    get_homedir();
892

893
    if (homedir == NULL)
894
	rcfile_error(N_("I can't find my home directory!  Wah!"));
895
896
897
898
    else {
	nanorc = charealloc(nanorc, strlen(homedir) + 9);
	sprintf(nanorc, "%s/.nanorc", homedir);

899
900
901
902
903
904
905
906
907
908
909
	/* 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");
910
	if (rcstream == NULL) {
911
912
	    /* Don't complain about the file's not existing. */
	    if (errno != ENOENT)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
913
914
		rcfile_error(N_("Error reading %s: %s"), nanorc,
			strerror(errno));
915
	} else
916
917
918
919
920
	    parse_rcfile(rcstream
#ifdef ENABLE_COLOR
		, FALSE
#endif
		);
921
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
922

923
924
    free(nanorc);
    nanorc = NULL;
925

926
927
928
#ifdef ENABLE_COLOR
    set_colorpairs();
#endif
929
930
}

931
#endif /* ENABLE_NANORC */