rcfile.c 11 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
#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

43
#define NUM_RCOPTS 20
44

45
/* Static stuff for the nanorc file */
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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},
    {"fill", 0},
    {"speller", 0},
    {"tempfile", TEMP_OPT},
    {"view", VIEW_MODE},
    {"nowrap", NO_WRAP},
    {"nohelp", NO_HELP},
    {"suspend", SUSPEND},
    {"multibuffer", MULTIBUFFER},
    {"smooth", SMOOTHSCROLL},
65
    {"keypad", ALT_KEYPAD},
66
    {"noconvert", NO_CONVERT}
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");
80
    fprintf(stderr, _("Error in %s on line %d: "), nanorc, lineno);
81
82
83
84
85
    va_start(ap, msg);
    vfprintf(stderr, msg, ap);
    va_end(ap);
    fprintf(stderr, _("\nPress return to continue starting nano\n"));

86
    while (getchar() != '\n');
87
88
89

}

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

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

}

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

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

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

133
134
135
    /* Null terminate and advance ptr */
    *ptr++ = 0;

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

139
    return ptr;
140

141
142
}

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

    if (colorname == NULL)
	return -1;

    if (strcasestr(colorname, "bright")) {
151
	*bright = 1;
152
153
	colorname += 6;
    }
154

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

    return mcolor;
}


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

    fgstr = ptr;
    ptr = parse_next_word(ptr);

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

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

206
207
    fg = colortoint(fgstr, &bright);
    bg = colortoint(bgstr, &bright);
208
209

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

212
    while (*ptr != '\0') {
213

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

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

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

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

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

276
	    ptr += 4;
277

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

285

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

296
297
	} else
	    tmpcolor->end = NULL;
298

299
    }
300
}
301
#endif				/* ENABLE_COLOR */
302

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

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

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

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

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

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

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

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

437

438
439
440
    if (getenv("HOME") == NULL)
	return;

441
    nanorc = charalloc(strlen(getenv("HOME")) + 10);
442
443
444
445
446
447
448
449
450
    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;
451
    }
452
453
454
455
456
457

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

458
    parse_rcfile(rcstream);
459
460
461
462
463
    fclose(rcstream);

}


464
#endif				/* ENABLE_NANORC */