rcfile.c 11.1 KB
Newer Older
1
2
/* $Id$ */
/**************************************************************************
3
 *   rcfile.c                                                             *
4
 *                                                                        *
5
 *   Copyright (C) 1999-2002 Chris Allegretta                             *
6
7
 *   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 *
8
 *   the Free Software Foundation; either version 2, or (at your option)  *
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 *   any later version.                                                   *
 *                                                                        *
 *   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.                         *
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
 *   along with this program; if not, write to the Free Software          *
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            *
 *                                                                        *
 **************************************************************************/

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "config.h"
#include "proto.h"
#include "nano.h"

#ifdef ENABLE_NANORC

#ifndef NANO_SMALL
#include <libintl.h>
#define _(string) gettext(string)
#else
#define _(string) (string)
#endif

42
#ifndef DISABLE_WRAPJUSTIFY
43
#define NUM_RCOPTS 19
44
#else
45
#define NUM_RCOPTS 18
46
47
#endif

48
/* Static stuff for the nanorc file */
49
50
51
52
53
54
55
56
57
58
rcoption rcopts[NUM_RCOPTS] = {
    {"regexp", USE_REGEXP},
    {"const", CONSTUPDATE},
    {"autoindent", AUTOINDENT},
    {"cut", CUT_TO_END},
    {"nofollow", FOLLOW_SYMLINKS},
    {"mouse", USE_MOUSE},
    {"operatingdir", 0},
    {"pico", PICO_MODE},
    {"tabsize", 0},
59
60

#ifndef DISABLE_WRAPJUSTIFY
61
    {"fill", 0},
62
63
#endif

64
65
66
67
68
69
70
71
72
73
    {"speller", 0},
    {"tempfile", TEMP_OPT},
    {"view", VIEW_MODE},
    {"nowrap", NO_WRAP},
    {"nohelp", NO_HELP},
    {"suspend", SUSPEND},
    {"multibuffer", MULTIBUFFER},
    {"smooth", SMOOTHSCROLL},
    {"keypad", ALT_KEYPAD}
};
74

75
76
77
78
static int errors = 0;
static int lineno = 0;
static char *nanorc;

79
80
/* We have an error in some part of the rcfile; put it on stderr and
  make the user hit return to continue starting up nano */
81
82
83
84
85
void rcfile_error(char *msg, ...)
{
    va_list ap;

    fprintf(stderr, "\n");
86
    fprintf(stderr, _("Error in %s on line %d: "), nanorc, lineno);
87
88
89
90
91
    va_start(ap, msg);
    vfprintf(stderr, msg, ap);
    va_end(ap);
    fprintf(stderr, _("\nPress return to continue starting nano\n"));

92
    while (getchar() != '\n');
93
94
95

}

96
/* Just print the error (one of many, perhaps) but don't abort, yet */
97
void rcfile_msg(char *msg, ...)
98
99
100
{
    va_list ap;

101
102
    if (!errors) {
	errors = 1;
103
104
105
106
107
108
109
110
111
	fprintf(stderr, "\n");
    }
    va_start(ap, msg);
    vfprintf(stderr, msg, ap);
    va_end(ap);
    fprintf(stderr, "\n");

}

112
/* Parse the next word from the string.  Returns NULL if we hit EOL */
113
114
char *parse_next_word(char *ptr)
{
115
116
117
118
119
120
121
122
123
124
125
126
127
128
    while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n' && *ptr != '\0')
	ptr++;

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

    /* Null terminate and advance ptr */
    *ptr++ = 0;

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

    return ptr;
}
129

130
131
132
133
134
char *parse_next_regex(char *ptr)
{
    char prev = ' ';
    while ((*ptr != '"' || prev == '\\') && *ptr != '\n' && *ptr != '\0') {
	prev = *ptr;
135
	ptr++;
136
    }
137

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

141
142
143
    /* Null terminate and advance ptr */
    *ptr++ = 0;

144
145
146
    while ((*ptr == ' ' || *ptr == '\t') && *ptr != '\0')
	ptr++;

147
    return ptr;
148

149
150
}

151
int colortoint(char *colorname, int *bright)
152
153
154
155
156
157
158
{
    int mcolor = 0;

    if (colorname == NULL)
	return -1;

    if (strcasestr(colorname, "bright")) {
159
	*bright = 1;
160
161
	colorname += 6;
    }
162

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
    if (!strcasecmp(colorname, "green"))
	mcolor += COLOR_GREEN;
    else if (!strcasecmp(colorname, "red"))
	mcolor += COLOR_RED;
    else if (!strcasecmp(colorname, "blue"))
	mcolor += COLOR_BLUE;
    else if (!strcasecmp(colorname, "white"))
	mcolor += COLOR_WHITE;
    else if (!strcasecmp(colorname, "yellow"))
	mcolor += COLOR_YELLOW;
    else if (!strcasecmp(colorname, "cyan"))
	mcolor += COLOR_CYAN;
    else if (!strcasecmp(colorname, "magenta"))
	mcolor += COLOR_MAGENTA;
    else if (!strcasecmp(colorname, "black"))
	mcolor += COLOR_BLACK;
    else {
180
181
182
183
	rcfile_error(_("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"));
184
185
186
187
188
189
190
191
192
	exit(1);
    }

    return mcolor;
}


#ifdef ENABLE_COLOR
/* Parse the color stuff into the colorstrings array */
193
void parse_colors(FILE * rcstream, char *buf, char *ptr)
194
{
195
196
    int fg, bg, bright = 0;
    int expectend = 0;		/* Do we expect an end= line? */
197
198
199
200
201
202
203
    char *tmp = NULL, *beginning, *fgstr, *bgstr;
    colortype *tmpcolor = NULL;

    fgstr = ptr;
    ptr = parse_next_word(ptr);

    if (ptr == NULL) {
204
	rcfile_error(_("Missing color name"));
205
206
207
208
209
210
211
212
213
	exit(1);
    }

    if (strstr(fgstr, ",")) {
	strtok(fgstr, ",");
	bgstr = strtok(NULL, ",");
    } else
	bgstr = NULL;

214
215
    fg = colortoint(fgstr, &bright);
    bg = colortoint(bgstr, &bright);
216
217

    /* Now the fun part, start adding regexps to individual strings
218
       in the colorstrings array, woo! */
219

220
    while (*ptr != '\0') {
221

222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
	while (*ptr == ' ')
	    ptr++;

	if (*ptr == '\n' || *ptr == '\0')
	    break;

	if (!strncasecmp(ptr, "start=", 6)) {
	    ptr += 6;
	    expectend = 1;
	}

	if (*ptr != '"') {
	    rcfile_error(_("regex strings must begin and end with a \" character\n"));
	    continue;
	}
	ptr++;
238

239
240
241
242
243
244
245
246
247
248
249
250
251
252
	beginning = ptr;
	ptr = parse_next_regex(ptr);

	tmp = NULL;
	tmp = mallocstrcpy(tmp, beginning);

	if (colorstrings == NULL) {
	    colorstrings = nmalloc(sizeof(colortype));
	    colorstrings->fg = fg;
	    colorstrings->bg = bg;
	    colorstrings->bright = bright;
	    colorstrings->start = tmp;
	    colorstrings->next = NULL;
	    tmpcolor = colorstrings;
253
#ifdef DEBUG
254
255
256
257
	    fprintf(stderr,
		    "Starting a new colorstring for fg %d bg %d\n",
		    fg, bg);
	    fprintf(stderr, "string val=%s\n", tmp);
258
259
#endif

260
261
262
	} else {
	    for (tmpcolor = colorstrings;
		 tmpcolor->next != NULL; tmpcolor = tmpcolor->next);
263
#ifdef DEBUG
264
265
	    fprintf(stderr, "Adding new entry for fg %d bg %d\n", fg, bg);
	    fprintf(stderr, "string val=%s\n", tmp);
266
#endif
267

268
269
270
271
272
273
274
275
	    tmpcolor->next = nmalloc(sizeof(colortype));
	    tmpcolor->next->fg = fg;
	    tmpcolor->next->bg = bg;
	    tmpcolor->next->bright = bright;
	    tmpcolor->next->start = tmp;
	    tmpcolor->next->next = NULL;
	    tmpcolor = tmpcolor->next;
	}
276

277
278
279
280
281
282
	if (expectend) {
	    if (ptr == NULL || strncasecmp(ptr, "end=", 4)) {
		rcfile_error(_
			     ("\n\t\"start=\" requires a corresponding \"end=\""));
		return;
	    }
283

284
	    ptr += 4;
285

286
287
288
289
	    if (*ptr != '"') {
		rcfile_error(_
			     ("regex strings must begin and end with a \" character\n"));
		continue;
290
291
292
	    }
	    ptr++;

293

294
295
	    beginning = ptr;
	    ptr = parse_next_regex(ptr);
296
#ifdef DEBUG
297
298
	    fprintf(stderr, "For end part, beginning = \"%s\"\n",
		    beginning);
299
#endif
300
301
302
	    tmp = NULL;
	    tmp = mallocstrcpy(tmp, beginning);
	    tmpcolor->end = tmp;
303

304
305
	} else
	    tmpcolor->end = NULL;
306

307
    }
308
}
309
#endif				/* ENABLE_COLOR */
310

311
/* Parse the RC file, once it has been opened successfully */
312
void parse_rcfile(FILE * rcstream)
313
314
{
    char *buf, *ptr, *keyword, *option;
315
    int set = 0, i;
316

317
    buf = charalloc(1024);
318
319
320
    while (fgets(buf, 1023, rcstream) > 0) {
	lineno++;
	ptr = buf;
321
322
	while ((*ptr == ' ' || *ptr == '\t') &&
	       (*ptr != '\n' && *ptr != '\0'))
323
324
325
326
327
328
329
330
331
	    ptr++;

	if (*ptr == '\n' || *ptr == '\0')
	    continue;

	if (*ptr == '#') {
#ifdef DEBUG
	    fprintf(stderr, _("parse_rcfile: Read a comment\n"));
#endif
332
	    continue;		/* Skip past commented lines */
333
334
335
336
337
338
339
340
341
342
343
344
345
	}

	/* Else skip to the next space */
	keyword = ptr;
	ptr = parse_next_word(ptr);
	if (!ptr)
	    continue;

	/* Else try to parse the keyword */
	if (!strcasecmp(keyword, "set"))
	    set = 1;
	else if (!strcasecmp(keyword, "unset"))
	    set = -1;
346
347
#ifdef ENABLE_COLOR
	else if (!strcasecmp(keyword, "color"))
348
	    parse_colors(rcstream, buf, ptr);
349
#endif				/* ENABLE_COLOR */
350
	else {
351
	    rcfile_msg(_("command %s not understood"), keyword);
352
353
354
355
356
357
358
359
360
	    continue;
	}

	option = ptr;
	ptr = parse_next_word(ptr);
	/* We don't care if ptr == NULL, as it should if using proper syntax */

	if (set != 0) {
	    for (i = 0; i <= NUM_RCOPTS - 1; i++) {
361
		if (!strcasecmp(option, rcopts[i].name)) {
362
#ifdef DEBUG
363
364
		    fprintf(stderr, _("parse_rcfile: Parsing option %s\n"),
			    rcopts[i].name);
365
366
#endif
		    if (set == 1 || rcopts[i].flag == FOLLOW_SYMLINKS) {
367
			if (!strcasecmp(rcopts[i].name, "operatingdir") ||
368
			    !strcasecmp(rcopts[i].name, "tabsize") ||
369
#ifndef DISABLE_WRAPJUSTIFY
370
			    !strcasecmp(rcopts[i].name, "fill") ||
371
372
373
374
#endif
#ifndef DISABLE_SPELLER
			    !strcasecmp(rcopts[i].name, "speller")
#else
375
			    0
376
#endif
377
			    ) {
378
379

			    if (*ptr == '\n' || *ptr == '\0') {
380
381
382
				rcfile_error(_
					     ("option %s requires an argument"),
					     rcopts[i].name);
383
384
385
386
387
				continue;
			    }
			    option = ptr;
			    ptr = parse_next_word(ptr);
			    if (!strcasecmp(rcopts[i].name, "fill")) {
388
389
#ifndef DISABLE_WRAPJUSTIFY

390
				if ((i = atoi(option)) < MIN_FILL_LENGTH) {
391
392
393
394
395
				    rcfile_error(_
						 ("requested fill size %d too small"),
						 i);
				} else
				    fill = i;
396
#endif
397
398
399
400
401
402
403
404
405
406
			    } else
				if (!strcasecmp(rcopts[i].name, "tabsize"))
			    {
				if ((i = atoi(option)) <= 0) {
				    rcfile_error(_
						 ("requested tab size %d too small"),
						 i);
				} else {
				    tabsize = i;
				}
407
			    } else {
408
#ifndef DISABLE_SPELLER
409
410
411
				alt_speller =
				    charalloc(strlen(option) + 1);
				strcpy(alt_speller, option);
412
#endif
413
			    }
414
			} else
415
416
			    SET(rcopts[i].flag);
#ifdef DEBUG
417
418
			fprintf(stderr, _("set flag %d!\n"),
				rcopts[i].flag);
419
420
421
422
#endif
		    } else {
			UNSET(rcopts[i].flag);
#ifdef DEBUG
423
424
			fprintf(stderr, _("unset flag %d!\n"),
				rcopts[i].flag);
425
#endif
426
		    }
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
		}
	    }
	}

    }
    if (errors)
	rcfile_error(_("Errors found in .nanorc file"));

    return;
}

/* The main rc file function, tries to open the rc file */
void do_rcfile(void)
{
    char *unable = _("Unable to open ~/.nanorc file, %s");
    struct stat fileinfo;
    FILE *rcstream;

445

446
447
448
    if (getenv("HOME") == NULL)
	return;

449
    nanorc = charalloc(strlen(getenv("HOME")) + 10);
450
451
452
453
454
455
456
457
458
    sprintf(nanorc, "%s/.nanorc", getenv("HOME"));

    if (stat(nanorc, &fileinfo) == -1) {

	/* Abort if the file doesn't exist and there's some other kind
	   of error stat()ing it */
	if (errno != ENOENT)
	    rcfile_error(unable, errno);
	return;
459
    }
460
461
462
463
464
465

    if ((rcstream = fopen(nanorc, "r")) == NULL) {
	rcfile_error(unable, strerror(errno));
	return;
    }

466
    parse_rcfile(rcstream);
467
468
469
470
471
    fclose(rcstream);

}


472
#endif				/* ENABLE_NANORC */