rcfile.c 11.6 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
 *   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>
23
#include <stdarg.h>
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#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

/* Static stuff for the nanorc file */
44
rcoption rcopts[] = {
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
    {"regexp", USE_REGEXP},
    {"const", CONSTUPDATE},
    {"autoindent", AUTOINDENT},
    {"cut", CUT_TO_END},
    {"nofollow", FOLLOW_SYMLINKS},
    {"mouse", USE_MOUSE},
    {"operatingdir", 0},
    {"pico", PICO_MODE},
    {"tabsize", 0},
    {"fill", 0},
    {"speller", 0},
    {"tempfile", TEMP_OPT},
    {"view", VIEW_MODE},
    {"nowrap", NO_WRAP},
    {"nohelp", NO_HELP},
    {"suspend", SUSPEND},
    {"multibuffer", MULTIBUFFER},
    {"smooth", SMOOTHSCROLL},
63
    {"keypad", ALT_KEYPAD},
64
65
66
    {"noconvert", NO_CONVERT},
    {"quotestr", 0},
    {"", 0}
67
};
68

69
70
71
72
static int errors = 0;
static int lineno = 0;
static char *nanorc;

73
74
/* 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 */
75
76
77
78
79
void rcfile_error(char *msg, ...)
{
    va_list ap;

    fprintf(stderr, "\n");
Chris Allegretta's avatar
Chris Allegretta committed
80
81
82
    if (lineno > 0)
	fprintf(stderr, _("Error in %s on line %d: "), nanorc, lineno);

83
84
85
86
87
    va_start(ap, msg);
    vfprintf(stderr, msg, ap);
    va_end(ap);
    fprintf(stderr, _("\nPress return to continue starting nano\n"));

88
    while (getchar() != '\n');
89
90
91

}

92
/* Just print the error (one of many, perhaps) but don't abort, yet */
93
void rcfile_msg(char *msg, ...)
94
95
96
{
    va_list ap;

97
98
    if (!errors) {
	errors = 1;
99
100
101
102
103
104
105
106
107
	fprintf(stderr, "\n");
    }
    va_start(ap, msg);
    vfprintf(stderr, msg, ap);
    va_end(ap);
    fprintf(stderr, "\n");

}

108
/* Parse the next word from the string.  Returns NULL if we hit EOL */
109
110
char *parse_next_word(char *ptr)
{
111
112
113
114
115
116
117
118
119
120
121
122
123
124
    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;
}
125

126
127
char *parse_next_regex(char *ptr)
{
128
129
    while ((*ptr != '"' || (*(ptr+1) != ' ' && *(ptr+1) != '\n')) 
	   && *ptr != '\n' && *ptr != '\0')
130
131
	ptr++;

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

135
136
137
    /* Null terminate and advance ptr */
    *ptr++ = 0;

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

141
    return ptr;
142

143
144
}

145
int colortoint(char *colorname, int *bright)
146
147
148
149
150
151
{
    int mcolor = 0;

    if (colorname == NULL)
	return -1;

152
    if (stristr(colorname, "bright")) {
153
	*bright = 1;
154
155
	colorname += 6;
    }
156

157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
    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 {
174
175
176
177
	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"));
178
179
180
181
182
183
184
185
186
	exit(1);
    }

    return mcolor;
}


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

    fgstr = ptr;
    ptr = parse_next_word(ptr);

    if (ptr == NULL) {
198
	rcfile_error(_("Missing color name"));
199
200
201
202
203
204
205
206
207
	exit(1);
    }

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

208
209
    fg = colortoint(fgstr, &bright);
    bg = colortoint(bgstr, &bright);
210
211

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

214
    while (*ptr != '\0') {
215

216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
	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++;
232

233
234
235
236
237
238
239
240
241
242
243
244
245
246
	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;
247
#ifdef DEBUG
248
249
250
251
	    fprintf(stderr,
		    "Starting a new colorstring for fg %d bg %d\n",
		    fg, bg);
	    fprintf(stderr, "string val=%s\n", tmp);
252
253
#endif

254
255
256
	} else {
	    for (tmpcolor = colorstrings;
		 tmpcolor->next != NULL; tmpcolor = tmpcolor->next);
257
#ifdef DEBUG
258
259
	    fprintf(stderr, "Adding new entry for fg %d bg %d\n", fg, bg);
	    fprintf(stderr, "string val=%s\n", tmp);
260
#endif
261

262
263
264
265
266
267
268
269
	    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;
	}
270

271
272
273
274
275
276
	if (expectend) {
	    if (ptr == NULL || strncasecmp(ptr, "end=", 4)) {
		rcfile_error(_
			     ("\n\t\"start=\" requires a corresponding \"end=\""));
		return;
	    }
277

278
	    ptr += 4;
279

280
281
282
283
	    if (*ptr != '"') {
		rcfile_error(_
			     ("regex strings must begin and end with a \" character\n"));
		continue;
284
285
286
	    }
	    ptr++;

287

288
289
	    beginning = ptr;
	    ptr = parse_next_regex(ptr);
290
#ifdef DEBUG
291
292
	    fprintf(stderr, "For end part, beginning = \"%s\"\n",
		    beginning);
293
#endif
294
295
296
	    tmp = NULL;
	    tmp = mallocstrcpy(tmp, beginning);
	    tmpcolor->end = tmp;
297

298
299
	} else
	    tmpcolor->end = NULL;
300

301
    }
302
}
303
#endif				/* ENABLE_COLOR */
304

305
/* Parse the RC file, once it has been opened successfully */
306
void parse_rcfile(FILE * rcstream)
307
308
{
    char *buf, *ptr, *keyword, *option;
309
    int set = 0, i, j;
310

311
    buf = charalloc(1024);
312
313
314
    while (fgets(buf, 1023, rcstream) > 0) {
	lineno++;
	ptr = buf;
315
316
	while ((*ptr == ' ' || *ptr == '\t') &&
	       (*ptr != '\n' && *ptr != '\0'))
317
318
319
320
321
322
323
324
325
	    ptr++;

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

	if (*ptr == '#') {
#ifdef DEBUG
	    fprintf(stderr, _("parse_rcfile: Read a comment\n"));
#endif
326
	    continue;		/* Skip past commented lines */
327
328
329
330
331
332
333
334
335
336
337
338
339
	}

	/* 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;
340
341
#ifdef ENABLE_COLOR
	else if (!strcasecmp(keyword, "color"))
342
	    parse_colors(rcstream, buf, ptr);
343
#endif				/* ENABLE_COLOR */
344
	else {
345
	    rcfile_msg(_("command %s not understood"), keyword);
346
347
348
349
350
351
352
353
	    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) {
354
	    for (i = 0; rcopts[i].name != ""; i++) {
355
		if (!strcasecmp(option, rcopts[i].name)) {
356
#ifdef DEBUG
357
358
		    fprintf(stderr, _("parse_rcfile: Parsing option %s\n"),
			    rcopts[i].name);
359
360
#endif
		    if (set == 1 || rcopts[i].flag == FOLLOW_SYMLINKS) {
361
			if (!strcasecmp(rcopts[i].name, "operatingdir") ||
362
			    !strcasecmp(rcopts[i].name, "tabsize") ||
363
#ifndef DISABLE_WRAPJUSTIFY
364
			    !strcasecmp(rcopts[i].name, "fill") ||
365
#endif
366
367
368
#ifndef DISABLE_JUSTIFY
			    !strcasecmp(rcopts[i].name, "quotestr") ||
#endif
369
370
371
#ifndef DISABLE_SPELLER
			    !strcasecmp(rcopts[i].name, "speller")
#else
372
			    0
373
#endif
374
			    ) {
375
376

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

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

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

    if (getenv("HOME") == NULL)
	return;

454
455
456
457
458
459
460
461
462
463
464
465
    nanorc = charalloc(strlen(SYSCONFDIR) + 10);
    sprintf(nanorc, "%s/nanorc", SYSCONFDIR);

    /* Try to open system nanorc */
    if (stat(nanorc, &fileinfo) != -1)
	if ((rcstream = fopen(nanorc, "r")) != NULL) {

	   /* Parse it! */
	    parse_rcfile(rcstream);
	    fclose(rcstream);
	}

466
    nanorc = charalloc(strlen(getenv("HOME")) + 10);
467
468
    sprintf(nanorc, "%s/.nanorc", getenv("HOME"));

Chris Allegretta's avatar
Chris Allegretta committed
469
    lineno = 0;
470
471
472
473
474
    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)
Chris Allegretta's avatar
Chris Allegretta committed
475
	    rcfile_error(unable, strerror(errno));
476
	return;
477
    }
478
479
480
481
482
483

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

484
    parse_rcfile(rcstream);
485
486
487
488
489
    fclose(rcstream);

}


490
#endif				/* ENABLE_NANORC */