blob: 56c66519126df56b1c28ebff513ff76c114fbf6c [file] [log] [blame]
Chris Allegretta8d8e0122001-04-18 04:28:54 +00001/* $Id$ */
2/**************************************************************************
Chris Allegrettaa9434802001-05-05 15:02:27 +00003 * rcfile.c *
Chris Allegretta8d8e0122001-04-18 04:28:54 +00004 * *
Jordi Mallach8ae57892002-01-04 17:57:40 +00005 * Copyright (C) 1999-2002 Chris Allegretta *
Chris Allegretta8d8e0122001-04-18 04:28:54 +00006 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
Chris Allegretta3a24f3f2001-10-24 11:33:54 +00008 * the Free Software Foundation; either version 2, or (at your option) *
Chris Allegretta8d8e0122001-04-18 04:28:54 +00009 * any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the Free Software *
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
19 * *
20 **************************************************************************/
21
22#include <stdlib.h>
Chris Allegretta34f80982002-01-22 20:09:20 +000023#include <stdarg.h>
Chris Allegretta8d8e0122001-04-18 04:28:54 +000024#include <string.h>
25#include <stdio.h>
26#include <errno.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <fcntl.h>
30#include "config.h"
31#include "proto.h"
32#include "nano.h"
33
34#ifdef ENABLE_NANORC
35
36#ifndef NANO_SMALL
37#include <libintl.h>
38#define _(string) gettext(string)
39#else
40#define _(string) (string)
41#endif
42
Chris Allegrettab3655b42001-10-22 03:15:31 +000043#ifndef DISABLE_WRAPJUSTIFY
Chris Allegretta6c1e6612002-01-19 16:52:34 +000044#define NUM_RCOPTS 19
Chris Allegrettab3655b42001-10-22 03:15:31 +000045#else
Chris Allegretta6c1e6612002-01-19 16:52:34 +000046#define NUM_RCOPTS 18
Chris Allegrettab3655b42001-10-22 03:15:31 +000047#endif
48
Chris Allegretta8d8e0122001-04-18 04:28:54 +000049/* Static stuff for the nanorc file */
Chris Allegretta6c1e6612002-01-19 16:52:34 +000050rcoption rcopts[NUM_RCOPTS] = {
51 {"regexp", USE_REGEXP},
52 {"const", CONSTUPDATE},
53 {"autoindent", AUTOINDENT},
54 {"cut", CUT_TO_END},
55 {"nofollow", FOLLOW_SYMLINKS},
56 {"mouse", USE_MOUSE},
57 {"operatingdir", 0},
58 {"pico", PICO_MODE},
59 {"tabsize", 0},
Chris Allegretta6fe61492001-05-21 12:56:25 +000060
61#ifndef DISABLE_WRAPJUSTIFY
Chris Allegretta6c1e6612002-01-19 16:52:34 +000062 {"fill", 0},
Chris Allegretta6fe61492001-05-21 12:56:25 +000063#endif
64
Chris Allegretta6c1e6612002-01-19 16:52:34 +000065 {"speller", 0},
66 {"tempfile", TEMP_OPT},
67 {"view", VIEW_MODE},
68 {"nowrap", NO_WRAP},
69 {"nohelp", NO_HELP},
70 {"suspend", SUSPEND},
71 {"multibuffer", MULTIBUFFER},
72 {"smooth", SMOOTHSCROLL},
73 {"keypad", ALT_KEYPAD}
74};
Chris Allegretta8d8e0122001-04-18 04:28:54 +000075
Chris Allegrettaf478f832002-01-18 21:54:35 +000076static int errors = 0;
77static int lineno = 0;
78static char *nanorc;
79
Chris Allegretta88520c92001-05-05 17:45:54 +000080/* We have an error in some part of the rcfile; put it on stderr and
81 make the user hit return to continue starting up nano */
Chris Allegretta8d8e0122001-04-18 04:28:54 +000082void rcfile_error(char *msg, ...)
83{
84 va_list ap;
85
86 fprintf(stderr, "\n");
Chris Allegrettaf478f832002-01-18 21:54:35 +000087 fprintf(stderr, _("Error in %s on line %d: "), nanorc, lineno);
Chris Allegretta8d8e0122001-04-18 04:28:54 +000088 va_start(ap, msg);
89 vfprintf(stderr, msg, ap);
90 va_end(ap);
91 fprintf(stderr, _("\nPress return to continue starting nano\n"));
92
Chris Allegretta6c1e6612002-01-19 16:52:34 +000093 while (getchar() != '\n');
Chris Allegretta8d8e0122001-04-18 04:28:54 +000094
95}
96
Chris Allegretta88520c92001-05-05 17:45:54 +000097/* Just print the error (one of many, perhaps) but don't abort, yet */
Chris Allegrettaf478f832002-01-18 21:54:35 +000098void rcfile_msg(char *msg, ...)
Chris Allegretta8d8e0122001-04-18 04:28:54 +000099{
100 va_list ap;
101
Chris Allegrettaf478f832002-01-18 21:54:35 +0000102 if (!errors) {
103 errors = 1;
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000104 fprintf(stderr, "\n");
105 }
106 va_start(ap, msg);
107 vfprintf(stderr, msg, ap);
108 va_end(ap);
109 fprintf(stderr, "\n");
110
111}
112
Chris Allegretta88520c92001-05-05 17:45:54 +0000113/* Parse the next word from the string. Returns NULL if we hit EOL */
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000114char *parse_next_word(char *ptr)
115{
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000116 while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n' && *ptr != '\0')
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000117 ptr++;
118
Chris Allegretta13fd44b2002-01-02 13:59:11 +0000119 if (*ptr == '\0')
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000120 return NULL;
Chris Allegrettaf478f832002-01-18 21:54:35 +0000121
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000122 /* Null terminate and advance ptr */
123 *ptr++ = 0;
124
Chris Allegretta08893e02001-11-29 02:42:27 +0000125 while ((*ptr == ' ' || *ptr == '\t') && *ptr != '\0')
126 ptr++;
127
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000128 return ptr;
129}
130
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000131char *parse_next_regex(char *ptr)
132{
133 char prev = ' ';
134 while ((*ptr != '"' || prev == '\\') && *ptr != '\n' && *ptr != '\0') {
135 prev = *ptr;
136 ptr++;
137 }
138
139 if (*ptr == '\0')
140 return NULL;
141
142 /* Null terminate and advance ptr */
143 *ptr++ = 0;
144
145 while ((*ptr == ' ' || *ptr == '\t') && *ptr != '\0')
146 ptr++;
147
148 return ptr;
149
150}
151
152int colortoint(char *colorname, int *bright)
Chris Allegretta08893e02001-11-29 02:42:27 +0000153{
154 int mcolor = 0;
155
156 if (colorname == NULL)
157 return -1;
158
159 if (strcasestr(colorname, "bright")) {
Chris Allegretta2fa11b82001-12-02 04:55:44 +0000160 *bright = 1;
Chris Allegretta08893e02001-11-29 02:42:27 +0000161 colorname += 6;
162 }
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000163
Chris Allegretta08893e02001-11-29 02:42:27 +0000164 if (!strcasecmp(colorname, "green"))
165 mcolor += COLOR_GREEN;
166 else if (!strcasecmp(colorname, "red"))
167 mcolor += COLOR_RED;
168 else if (!strcasecmp(colorname, "blue"))
169 mcolor += COLOR_BLUE;
170 else if (!strcasecmp(colorname, "white"))
171 mcolor += COLOR_WHITE;
172 else if (!strcasecmp(colorname, "yellow"))
173 mcolor += COLOR_YELLOW;
174 else if (!strcasecmp(colorname, "cyan"))
175 mcolor += COLOR_CYAN;
176 else if (!strcasecmp(colorname, "magenta"))
177 mcolor += COLOR_MAGENTA;
178 else if (!strcasecmp(colorname, "black"))
179 mcolor += COLOR_BLACK;
180 else {
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000181 rcfile_error(_("color %s not understood.\n"
182 "Valid colors are \"green\", \"red\", \"blue\", \n"
183 "\"white\", \"yellow\", \"cyan\", \"magenta\" and \n"
184 "\"black\", with the optional prefix \"bright\".\n"));
Chris Allegretta08893e02001-11-29 02:42:27 +0000185 exit(1);
186 }
187
188 return mcolor;
189}
190
191
192#ifdef ENABLE_COLOR
193/* Parse the color stuff into the colorstrings array */
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000194void parse_colors(FILE * rcstream, char *buf, char *ptr)
Chris Allegretta08893e02001-11-29 02:42:27 +0000195{
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000196 int fg, bg, bright = 0;
197 int expectend = 0; /* Do we expect an end= line? */
Chris Allegretta08893e02001-11-29 02:42:27 +0000198 char *tmp = NULL, *beginning, *fgstr, *bgstr;
199 colortype *tmpcolor = NULL;
Chris Allegretta08893e02001-11-29 02:42:27 +0000200
201 fgstr = ptr;
202 ptr = parse_next_word(ptr);
203
204 if (ptr == NULL) {
Chris Allegrettaf478f832002-01-18 21:54:35 +0000205 rcfile_error(_("Missing color name"));
Chris Allegretta08893e02001-11-29 02:42:27 +0000206 exit(1);
207 }
208
209 if (strstr(fgstr, ",")) {
210 strtok(fgstr, ",");
211 bgstr = strtok(NULL, ",");
212 } else
213 bgstr = NULL;
214
Chris Allegrettaf478f832002-01-18 21:54:35 +0000215 fg = colortoint(fgstr, &bright);
216 bg = colortoint(bgstr, &bright);
Chris Allegretta08893e02001-11-29 02:42:27 +0000217
218 /* Now the fun part, start adding regexps to individual strings
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000219 in the colorstrings array, woo! */
Chris Allegretta08893e02001-11-29 02:42:27 +0000220
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000221 while (*ptr != '\0') {
Chris Allegrettaf478f832002-01-18 21:54:35 +0000222
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000223 while (*ptr == ' ')
Chris Allegretta08893e02001-11-29 02:42:27 +0000224 ptr++;
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000225
226 if (*ptr == '\n' || *ptr == '\0')
Chris Allegretta08893e02001-11-29 02:42:27 +0000227 break;
Chris Allegretta08893e02001-11-29 02:42:27 +0000228
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000229 if (!strncasecmp(ptr, "start=", 6)) {
230 ptr += 6;
231 expectend = 1;
Chris Allegrettaf478f832002-01-18 21:54:35 +0000232 }
233
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000234 if (*ptr != '"') {
235 rcfile_error(_("regex strings must begin and end with a \" character\n"));
236 continue;
237 }
238 ptr++;
239
Chris Allegrettaf478f832002-01-18 21:54:35 +0000240 beginning = ptr;
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000241 ptr = parse_next_regex(ptr);
242
Chris Allegrettaf478f832002-01-18 21:54:35 +0000243 tmp = NULL;
244 tmp = mallocstrcpy(tmp, beginning);
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000245
246 if (colorstrings == NULL) {
247 colorstrings = nmalloc(sizeof(colortype));
248 colorstrings->fg = fg;
249 colorstrings->bg = bg;
250 colorstrings->bright = bright;
251 colorstrings->start = tmp;
252 colorstrings->next = NULL;
253 tmpcolor = colorstrings;
254#ifdef DEBUG
255 fprintf(stderr,
256 "Starting a new colorstring for fg %d bg %d\n",
257 fg, bg);
258 fprintf(stderr, "string val=%s\n", tmp);
259#endif
260
261 } else {
262 for (tmpcolor = colorstrings;
263 tmpcolor->next != NULL; tmpcolor = tmpcolor->next);
264#ifdef DEBUG
265 fprintf(stderr, "Adding new entry for fg %d bg %d\n", fg, bg);
266 fprintf(stderr, "string val=%s\n", tmp);
267#endif
268
269 tmpcolor->next = nmalloc(sizeof(colortype));
270 tmpcolor->next->fg = fg;
271 tmpcolor->next->bg = bg;
272 tmpcolor->next->bright = bright;
273 tmpcolor->next->start = tmp;
274 tmpcolor->next->next = NULL;
275 tmpcolor = tmpcolor->next;
276 }
277
278 if (expectend) {
279 if (ptr == NULL || strncasecmp(ptr, "end=", 4)) {
280 rcfile_error(_
281 ("\n\t\"start=\" requires a corresponding \"end=\""));
282 return;
283 }
284
285 ptr += 4;
286
287 if (*ptr != '"') {
288 rcfile_error(_
289 ("regex strings must begin and end with a \" character\n"));
290 continue;
291 }
292 ptr++;
293
294
295 beginning = ptr;
296 ptr = parse_next_regex(ptr);
297#ifdef DEBUG
298 fprintf(stderr, "For end part, beginning = \"%s\"\n",
299 beginning);
300#endif
301 tmp = NULL;
302 tmp = mallocstrcpy(tmp, beginning);
303 tmpcolor->end = tmp;
304
305 } else
306 tmpcolor->end = NULL;
Chris Allegrettaf478f832002-01-18 21:54:35 +0000307
308 }
Chris Allegretta08893e02001-11-29 02:42:27 +0000309}
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000310#endif /* ENABLE_COLOR */
Chris Allegretta08893e02001-11-29 02:42:27 +0000311
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000312/* Parse the RC file, once it has been opened successfully */
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000313void parse_rcfile(FILE * rcstream)
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000314{
315 char *buf, *ptr, *keyword, *option;
Chris Allegrettaf478f832002-01-18 21:54:35 +0000316 int set = 0, i;
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000317
Chris Allegretta8d848af2001-05-18 04:44:16 +0000318 buf = charalloc(1024);
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000319 while (fgets(buf, 1023, rcstream) > 0) {
320 lineno++;
321 ptr = buf;
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000322 while ((*ptr == ' ' || *ptr == '\t') &&
323 (*ptr != '\n' && *ptr != '\0'))
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000324 ptr++;
325
326 if (*ptr == '\n' || *ptr == '\0')
327 continue;
328
329 if (*ptr == '#') {
330#ifdef DEBUG
331 fprintf(stderr, _("parse_rcfile: Read a comment\n"));
332#endif
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000333 continue; /* Skip past commented lines */
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000334 }
335
336 /* Else skip to the next space */
337 keyword = ptr;
338 ptr = parse_next_word(ptr);
339 if (!ptr)
340 continue;
341
342 /* Else try to parse the keyword */
343 if (!strcasecmp(keyword, "set"))
344 set = 1;
345 else if (!strcasecmp(keyword, "unset"))
346 set = -1;
Chris Allegretta08893e02001-11-29 02:42:27 +0000347#ifdef ENABLE_COLOR
348 else if (!strcasecmp(keyword, "color"))
Chris Allegrettaf478f832002-01-18 21:54:35 +0000349 parse_colors(rcstream, buf, ptr);
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000350#endif /* ENABLE_COLOR */
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000351 else {
Chris Allegrettaf478f832002-01-18 21:54:35 +0000352 rcfile_msg(_("command %s not understood"), keyword);
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000353 continue;
354 }
355
356 option = ptr;
357 ptr = parse_next_word(ptr);
358 /* We don't care if ptr == NULL, as it should if using proper syntax */
359
360 if (set != 0) {
361 for (i = 0; i <= NUM_RCOPTS - 1; i++) {
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000362 if (!strcasecmp(option, rcopts[i].name)) {
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000363#ifdef DEBUG
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000364 fprintf(stderr, _("parse_rcfile: Parsing option %s\n"),
365 rcopts[i].name);
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000366#endif
367 if (set == 1 || rcopts[i].flag == FOLLOW_SYMLINKS) {
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000368 if (!strcasecmp(rcopts[i].name, "operatingdir") ||
Chris Allegretta66c33b82001-10-02 23:57:31 +0000369 !strcasecmp(rcopts[i].name, "tabsize") ||
Chris Allegretta6fe61492001-05-21 12:56:25 +0000370#ifndef DISABLE_WRAPJUSTIFY
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000371 !strcasecmp(rcopts[i].name, "fill") ||
Chris Allegretta6fe61492001-05-21 12:56:25 +0000372#endif
373#ifndef DISABLE_SPELLER
374 !strcasecmp(rcopts[i].name, "speller")
375#else
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000376 0
Chris Allegretta6fe61492001-05-21 12:56:25 +0000377#endif
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000378 ) {
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000379
380 if (*ptr == '\n' || *ptr == '\0') {
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000381 rcfile_error(_
382 ("option %s requires an argument"),
383 rcopts[i].name);
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000384 continue;
385 }
386 option = ptr;
387 ptr = parse_next_word(ptr);
388 if (!strcasecmp(rcopts[i].name, "fill")) {
Chris Allegretta6fe61492001-05-21 12:56:25 +0000389#ifndef DISABLE_WRAPJUSTIFY
390
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000391 if ((i = atoi(option)) < MIN_FILL_LENGTH) {
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000392 rcfile_error(_
393 ("requested fill size %d too small"),
394 i);
395 } else
396 fill = i;
Chris Allegretta6fe61492001-05-21 12:56:25 +0000397#endif
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000398 } else
399 if (!strcasecmp(rcopts[i].name, "tabsize"))
400 {
401 if ((i = atoi(option)) <= 0) {
402 rcfile_error(_
403 ("requested tab size %d too small"),
404 i);
405 } else {
406 tabsize = i;
407 }
Chris Allegretta66c33b82001-10-02 23:57:31 +0000408 } else {
Chris Allegretta6fe61492001-05-21 12:56:25 +0000409#ifndef DISABLE_SPELLER
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000410 alt_speller =
411 charalloc(strlen(option) + 1);
412 strcpy(alt_speller, option);
Chris Allegretta6fe61492001-05-21 12:56:25 +0000413#endif
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000414 }
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000415 } else
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000416 SET(rcopts[i].flag);
417#ifdef DEBUG
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000418 fprintf(stderr, _("set flag %d!\n"),
419 rcopts[i].flag);
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000420#endif
421 } else {
422 UNSET(rcopts[i].flag);
423#ifdef DEBUG
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000424 fprintf(stderr, _("unset flag %d!\n"),
425 rcopts[i].flag);
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000426#endif
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000427 }
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000428 }
429 }
430 }
431
432 }
433 if (errors)
434 rcfile_error(_("Errors found in .nanorc file"));
435
436 return;
437}
438
439/* The main rc file function, tries to open the rc file */
440void do_rcfile(void)
441{
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000442 char *unable = _("Unable to open ~/.nanorc file, %s");
443 struct stat fileinfo;
444 FILE *rcstream;
445
Chris Allegretta08893e02001-11-29 02:42:27 +0000446
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000447 if (getenv("HOME") == NULL)
448 return;
449
Chris Allegretta88b09152001-05-17 11:35:43 +0000450 nanorc = charalloc(strlen(getenv("HOME")) + 10);
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000451 sprintf(nanorc, "%s/.nanorc", getenv("HOME"));
452
453 if (stat(nanorc, &fileinfo) == -1) {
454
455 /* Abort if the file doesn't exist and there's some other kind
456 of error stat()ing it */
457 if (errno != ENOENT)
458 rcfile_error(unable, errno);
459 return;
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000460 }
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000461
462 if ((rcstream = fopen(nanorc, "r")) == NULL) {
463 rcfile_error(unable, strerror(errno));
464 return;
465 }
466
Chris Allegrettaf478f832002-01-18 21:54:35 +0000467 parse_rcfile(rcstream);
Chris Allegretta8d8e0122001-04-18 04:28:54 +0000468 fclose(rcstream);
469
470}
471
472
Chris Allegretta6c1e6612002-01-19 16:52:34 +0000473#endif /* ENABLE_NANORC */