use split CVS repo address, like in dietlibc
[alioth/jupp.git] / rc.c
1 /*
2  *      *rc file parser
3  *      Copyright
4  *              (C) 1992 Joseph H. Allen;
5  *
6  *      This file is part of JOE (Joe's Own Editor)
7  */
8 #define EXTERN_RC_C
9 #include "config.h"
10 #include "types.h"
11
12 __RCSID("$MirOS: contrib/code/jupp/rc.c,v 1.47 2018/02/14 17:45:30 tg Exp $");
13
14 #include <string.h>
15 #include <stdlib.h>
16
17 #ifdef HAVE_BSD_STRING_H
18 #include <bsd/string.h>
19 #endif
20
21 #include "builtin.h"
22 #include "cmd.h"
23 #include "kbd.h"
24 #include "macro.h"
25 #include "menu.h"
26 #include "path.h"
27 #include "pw.h"
28 #include "rc.h"
29 #include "regex.h"
30 #include "tw.h"
31 #include "uedit.h"
32 #include "umath.h"
33 #include "utils.h"
34 #include "vs.h"
35 #include "b.h"
36 #include "syntax.h"
37 #include "va.h"
38 #include "charmap.h"
39 #include "w.h"
40
41 #define OPT_BUF_SIZE    60
42
43 /* List of named contexts */
44 static struct context {
45         struct context *next;
46         unsigned char *name;
47         KMAP *kmap;
48 } *contexts = NULL;
49
50 /*
51  * Find a context of a given name; if not found,
52  * one with an empty kmap is created.
53  */
54 KMAP *
55 kmap_getcontext(const unsigned char *name, int docreate)
56 {
57         struct context *c;
58
59         for (c = contexts; c; c = c->next)
60                 if (!strcmp(c->name, name))
61                         return c->kmap;
62
63         if (!docreate)
64                 return (NULL);
65
66         c = malloc(sizeof(struct context));
67
68         c->next = contexts;
69         c->name = (unsigned char *)strdup((char *)name);
70         contexts = c;
71         return c->kmap = mkkmap();
72 }
73
74 OPTIONS *options = NULL;
75
76 /* Global variable options */
77 extern int mid, dspasis, dspctrl, help, square, csmode, nobackups, lightoff, exask, skiptop;
78 extern int noxon, lines, columns, Baud, dopadding, orphan, marking, keepup, nonotice;
79 extern int notite, pastetite, usetabs, assume_color, guesscrlf, guessindent, menu_explorer, icase, wrap, autoswap;
80 extern unsigned char *backpath;
81
82 /* Default options for prompt windows */
83 OPTIONS pdefault = {
84         NULL,           /* *next */
85         NULL,           /* *name_regex */
86         NULL,           /* *contents_regex */
87         0,              /* overtype */
88         0,              /* lmargin */
89         76,             /* rmargin */
90         0,              /* autoindent */
91         0,              /* wordwrap */
92         8,              /* tab */
93         ' ',            /* indent char */
94         1,              /* indent step */
95         NULL,           /* *context */
96         NULL,           /* *lmsg */
97         NULL,           /* *rmsg */
98         NULL,           /* *hmsg */
99         0,              /* line numbers */
100         0,              /* read only */
101         0,              /* french spacing */
102         0,              /* spaces */
103         0,              /* crlf */
104         0,              /* Highlight */
105         NULL,           /* Syntax name */
106         NULL,           /* Syntax */
107         NULL,           /* Name of character set */
108         NULL,           /* Character set */
109         0,              /* Smart home key */
110         0,              /* Goto indent first */
111         0,              /* Smart backspace key */
112         0,              /* Purify indentation */
113         0,              /* Picture mode */
114         NULL,           /* macro to execute for new files */
115         NULL,           /* macro to execute for existing files */
116         NULL,           /* macro to execute before saving new files */
117         NULL,           /* macro to execute before saving existing files */
118         0,              /* visible spaces */
119         0               /* hex */
120 };
121
122 /* Default options for file windows */
123 char main_context[] = "main";
124 OPTIONS fdefault = {
125         NULL,           /* *next */
126         NULL,           /* *name_regex */
127         NULL,           /* *contents_regex */
128         0,              /* overtype */
129         0,              /* lmargin */
130         76,             /* rmargin */
131         0,              /* autoindent */
132         0,              /* wordwrap */
133         8,              /* tab */
134         ' ',            /* indent char */
135         1,              /* indent step */
136         US main_context,                /* *context */
137         UC "\\i%n %m %M",               /* *lmsg */
138         UC " %S Ctrl-K H for help",     /* *rmsg */
139         NULL,           /* *hmsg */
140         0,              /* line numbers */
141         0,              /* read only */
142         0,              /* french spacing */
143         0,              /* spaces */
144         0,              /* crlf */
145         0,              /* Highlight */
146         NULL,           /* Syntax name */
147         NULL,           /* Syntax */
148         NULL,           /* Name of character set */
149         NULL,           /* Character set */
150         0,              /* Smart home key */
151         0,              /* Goto indent first */
152         0,              /* Smart backspace key */
153         0,              /* Purity indentation */
154         0,              /* Picture mode */
155         NULL, NULL, NULL, NULL, /* macros (see above) */
156         0,              /* visible spaces */
157         0               /* hex */
158 };
159
160 /* Update options */
161 void
162 lazy_opts(OPTIONS *o)
163 {
164         o->syntax = load_dfa(o->syntax_name);
165         o->charmap = find_charmap(o->map_name);
166         if (!o->charmap)
167                 o->charmap = fdefault.charmap;
168         /* Hex not allowed with UTF-8 */
169         if (o->hex && o->charmap->type) {
170                 o->charmap = find_charmap(UC "c");
171         }
172 }
173
174 /* Set local options depending on file name and contents */
175 void
176 setopt(B *b, const unsigned char *parsed_name)
177 {
178         OPTIONS *o;
179         int x;
180         unsigned char *pieces[26];
181         P *p;
182
183         for (x = 0; x != 26; ++x)
184                 pieces[x] = NULL;
185
186         for (o = options; o; o = o->next)
187                 if (rmatch(o->name_regex, parsed_name)) {
188                         if (!o->contents_regex)
189                                 goto done;
190                         p = pdup(b->bof);
191                         x = pmatch(pieces, o->contents_regex,
192                             strlen((char *)o->contents_regex), p, 0, 0);
193                         prm(p);
194                         if (x)
195                                 goto done;
196                 }
197
198         b->o = fdefault;
199         if (0) {
200  done:
201                 b->o = *o;
202         }
203         lazy_opts(&b->o);
204
205         for (x = 0; x != 26; ++x)
206                 vsrm(pieces[x]);
207 }
208
209 /* Table of options and how to set them */
210
211 /*
212  * local means it's in an OPTION structure,
213  * global means it's in a global variable
214  */
215 #define F(x) NULL, &fdefault.x
216 #define G(type,name,setiaddr,yes,no,menu,low,high) \
217         X(type,name,setiaddr,yes,no,menu,low,high)
218 #define X(type,name,seti,addr,yes,no,menu,low,high) \
219         { UC name, { seti }, US addr, UC yes, UC no, UC menu, 0, type, low, high }
220 #define L(x) &x, NULL
221 static struct glopts {
222         const unsigned char *name;      /* Option name */
223         union {
224                 int *i;
225                 unsigned char **us;
226         } set;                          /* Address of global option */
227         unsigned char *addr;            /* Local options structure member address */
228         const unsigned char *yes;       /* Message if option was turned on, or prompt string */
229         const unsigned char *no;        /* Message if option was turned off */
230         const unsigned char *menu;      /* Menu string */
231         size_t ofst;                    /* Local options structure member offset */
232         int type;               /* 0 for global option flag
233                                    1 for global option numeric
234                                    2 for global option string
235                                    4 for local option flag
236                                    5 for local option numeric
237                                    6 for local option string
238                                    7 for local option numeric+1, with range checking
239                                    8 for ...?
240                                    9 for syntax
241                                   13 for encoding
242                                  */
243         int low;                /* Low limit for numeric options */
244         int high;               /* High limit for numeric options */
245 } glopts[] = {
246 G( 0, "noxon",          L(noxon),         "XON/XOFF processing disabled", "XON/XOFF processing enabled", "  XON/XOFF usable ", 0, 0),
247 G( 0, "keepup",         L(keepup),        "Status line updated constantly", "Status line updated once/sec", "  Fast status line ", 0, 0),
248 G( 1, "baud",           L(Baud),          "Terminal baud rate (%d): ", NULL, "  Baud rate ", 0, 38400),
249 G( 4, "overwrite",      F(overtype),      "Overtype mode", "Insert mode", "T Overtype ", 0, 0),
250 G( 4, "autoindent",     F(autoindent),    "Autoindent enabled", "Autoindent disabled", "I Autoindent ", 0, 0),
251 G( 4, "wordwrap",       F(wordwrap),      "Wordwrap enabled", "Wordwrap disabled", "Word wrap ", 0, 0),
252 G( 5, "tab",            F(tab),           "Tab width (%d): ", NULL, "D Tab width ", 1, 64),
253 G( 7, "lmargin",        F(lmargin),       "Left margin (%d): ", NULL, "Left margin ", 0, 63),
254 G( 7, "rmargin",        F(rmargin),       "Right margin (%d): ", NULL, "Right margin ", 7, 255),
255 G( 0, "square",         L(square),        "Rectangle mode", "Text-stream mode", "X Rectangle mode ", 0, 0),
256 G( 0, "icase",          L(icase),         "Ignore case by default", "Case sensitive by default", "  Case insensitive ", 0, 0),
257 G( 0, "wrap",           L(wrap),          "Search wraps", "Search doesn't wrap", "  Search wraps ", 0, 0),
258 G( 0, "menu_explorer",  L(menu_explorer), "Menu explorer mode", "  Simple completion", "  Menu explorer ", 0, 0),
259 G( 0, "autoswap",       L(autoswap),      "Autoswap ^KB and ^KK", "  Autoswap off ", "  Autoswap mode ", 0, 0),
260 G( 5, "indentc",        F(indentc),       "Indent char %d (SPACE=32, TAB=9, ^C to abort): ", NULL, "  Indent char ", 0, 255),
261 G( 5, "istep",          F(istep),         "Indent step %d (^C to abort): ", NULL, "  Indent step ", 1, 64),
262 G( 4, "french",         F(french),        "One space after periods for paragraph reformat", "Two spaces after periods for paragraph reformat", "  French spacing ", 0, 0),
263 G( 4, "highlight",      F(highlight),     "Highlighting enabled", "Highlighting disabled", "Highlighting ", 0, 0),
264 G( 4, "spaces",         F(spaces),        "Inserting spaces when tab key is hit", "Inserting tabs when tab key is hit", "  Disable tabs ", 0, 0),
265 G( 0, "mid",            L(mid),           "Cursor will be recentered on scrolls", "Cursor will not be recentered on scroll", "Center on scroll ", 0, 0),
266 G( 0, "guess_crlf",     L(guesscrlf),     "Automatically detect MS-DOS files", "Do not automatically detect MS-DOS files", "  Auto detect CR-LF ", 0, 0),
267 G( 0, "guess_indent",   L(guessindent),   "Automatically detect indentation", "Do not automatically detect indentation", "  Guess indent ", 0, 0),
268 G( 4, "crlf",           F(crlf),          "CR-LF is line terminator", "LF is line terminator", "Z CR-LF (MS-DOS) ", 0, 0),
269 G( 4, "linums",         F(linums),        "Line numbers enabled", "Line numbers disabled", "N Line numbers ", 0, 0),
270 G( 0, "marking",        L(marking),       "Anchored block marking on", "Anchored block marking off", "Marking ", 0, 0),
271 G( 0, "asis",           L(dspasis),       "Characters above 127 shown as-is", "Characters above 127 shown in inverse", "  Meta chars as-is ", 0, 0),
272 G( 0, "force",          L(force),         "Last line forced to have NL when file saved", "Last line not forced to have NL", "Force last NL ", 0, 0),
273 G( 0, "nobackups",      L(nobackups),     "Backup files will not be made", "Backup files will be made", "  Disable backups ", 0, 0),
274 G( 0, "lightoff",       L(lightoff),      "Highlighting turned off after block operations", "Highlighting not turned off after block operations", "Auto unmark ", 0, 0),
275 G( 0, "exask",          L(exask),         "Prompt for filename in save & exit command", "Don't prompt for filename in save & exit command", "  Exit ask ", 0, 0),
276 G( 0, "beep",           L(dobeep),        "Warning bell enabled", "Warning bell disabled", "Beeps ", 0, 0),
277 G( 0, "nosta",          L(staen),         "Top-most status line disabled", "Top-most status line enabled", "  Disable status ", 0, 0),
278 G( 1, "pg",             L(pgamnt),        "Lines to keep for PgUp/PgDn or -1 for 1/2 window (%d): ", NULL, "  # PgUp/PgDn lines ", -1, 64),
279 G( 0, "csmode",         L(csmode),        "Start search after a search repeats previous search", "Start search always starts a new search", "Continued search ", 0, 0),
280 G( 4, "rdonly",         F(readonly),      "Read only", "Full editing", "O Read only ", 0, 0),
281 G( 4, "smarthome",      F(smarthome),     "Smart home key enabled", "Smart home key disabled", "  Smart home key ", 0, 0),
282 G( 4, "indentfirst",    F(indentfirst),   "Smart home goes to indent first", "Smart home goes home first", "  To indent first ", 0, 0),
283 G( 4, "smartbacks",     F(smartbacks),    "Smart backspace key enabled", "Smart backspace key disabled", "  Smart backspace ", 0, 0),
284 G( 4, "purify",         F(purify),        "Indentation clean up enabled", "Indentation clean up disabled", "  Clean up indents ", 0, 0),
285 G( 4, "picture",        F(picture),       "Picture drawing mode enabled", "Picture drawing mode disabled", "Picture mode ", 0, 0),
286 X( 2, "backpath",       NULL, NULL,       "Backup files stored in (%s): ", NULL, "  Backup file path ", 0, 0),
287 G( 4, "vispace",        F(vispace),       "Spaces visible", "Spaces invisible", "Visible spaces ", 0, 0),
288 G( 4, "hex",            F(hex),           "Hex edit mode", "Text edit mode", "G Hexedit mode ", 0, 0),
289 X( 9, "syntax",         NULL, NULL,       "Select syntax (%s; ^C to abort): ", NULL, "Y Syntax", 0, 0),
290 X(13, "encoding",       NULL, NULL,       "Select file character set (%s; ^C to abort): ", NULL, "Encoding ", 0, 0),
291 G( 0, "nonotice",       L(nonotice),      NULL, NULL, NULL, 0, 0),
292 G( 0, "orphan",         L(orphan),        NULL, NULL, NULL, 0, 0),
293 G( 0, "help",           L(help),          NULL, NULL, NULL, 0, 0),
294 G( 0, "dopadding",      L(dopadding),     NULL, NULL, NULL, 0, 0),
295 G( 1, "lines",          L(lines),         NULL, NULL, NULL, 2, 1024),
296 G( 1, "columns",        L(columns),       NULL, NULL, NULL, 2, 1024),
297 G( 1, "skiptop",        L(skiptop),       NULL, NULL, NULL, 0, 64),
298 G( 0, "notite",         L(notite),        NULL, NULL, NULL, 0, 0),
299 G( 0, "pastetite",      L(pastetite),     NULL, NULL, NULL, 0, 0),
300 G( 0, "usetabs",        L(usetabs),       NULL, NULL, NULL, 0, 0),
301 G( 0, "assume_color",   L(assume_color),  NULL, NULL, NULL, 0, 0),
302 X( 0, NULL,             NULL, NULL,       NULL, NULL, NULL, 0, 0)
303 };
304 #undef F
305 #undef G
306 #undef L
307 #undef X
308
309 /* Initialize .ofsts above.  Is this really necessary? */
310
311 int isiz = 0;
312
313 static void izopts(void)
314 {
315         int x;
316
317         for (x = 0; glopts[x].name; ++x)
318                 switch (glopts[x].type) {
319                 case 2:
320                         if (!strcmp((const char *)glopts[x].name, "backpath"))
321                                 glopts[x].set.us = &backpath;
322                         break;
323                 case 4:
324                 case 5:
325                 case 6:
326                 case 7:
327                 case 8:
328                         glopts[x].ofst = glopts[x].addr - (unsigned char *) &fdefault;
329                 }
330         isiz = 1;
331 }
332 #define RELOPT(lopts, opt) (*((int *)(((unsigned char *)(lopts)) + glopts[opt].ofst)))
333
334 /*-
335  * Set a global or local option:
336  * 's' is option name
337  * 'arg' is a possible argument string (taken only if option has an arg)
338  * 'options' points to options structure to modify (can be NULL).
339  * 'set'==0: set only in 'options' if it's given.
340  * 'set'!=0: set global variable option.
341  * return value: no. of fields taken (1 or 2), or 0 if option not found.
342  *
343  * So this function is used both to set options, and to parse over options
344  * without setting them.
345  *
346  * These combinations are used:
347  *
348  * glopt(name,arg,NULL,1): set global variable option
349  * glopt(name,arg,NULL,0): parse over option
350  * glopt(name,arg,options,0): set file local option
351  * glopt(name,arg,&fdefault,1): set default file options
352  * glopt(name,arg,options,1): set file local option
353  */
354 int
355 glopt(unsigned char *s, unsigned char *arg, OPTIONS *opts, int set)
356 {
357         int val;
358         int ret = 0;
359         int st = 1;     /* 1 to set option, 0 to clear it */
360         int x;
361         void *vp;
362
363         /* Initialize offsets */
364         if (!isiz)
365                 izopts();
366
367         /* Clear instead of set? */
368         if (s[0] == '-') {
369                 st = 0;
370                 ++s;
371         }
372
373         for (x = 0; glopts[x].name; ++x)
374                 if (!strcmp(glopts[x].name, s)) {
375                         switch (glopts[x].type) {
376                         case 0:
377                                 /* Global variable flag option */
378                                 if (set)
379                                         *glopts[x].set.i = st;
380                                 break;
381                         case 1:
382                                 /* Global variable integer option */
383                                 if (set && arg) {
384                                         val = ustolb(arg, &vp, glopts[x].low, glopts[x].high, USTOL_TRIM | USTOL_EOS);
385                                         if (vp)
386                                                 *glopts[x].set.i = val;
387                                 }
388                                 break;
389                         case 2:
390                                 /* Global variable string option */
391                                 if (set)
392                                         *glopts[x].set.us = arg ? (unsigned char *)strdup((char *)arg) : NULL;
393                                 break;
394                         case 4:
395                                 /* Local option flag */
396                                 if (opts)
397                                         RELOPT(opts, x) = st;
398                                 break;
399                         case 5:
400                                 /* Local option integer */
401                                 if (arg && opts) {
402                                         val = ustolb(arg, &vp, glopts[x].low, glopts[x].high, USTOL_TRIM | USTOL_EOS);
403                                         if (vp)
404                                                 RELOPT(opts, x) = val;
405                                 }
406                                 break;
407                         case 7:
408                                 /* Local option numeric + 1, with range checking */
409                                 if (arg) {
410                                         val = ustolb(arg, &vp, glopts[x].low, glopts[x].high, USTOL_TRIM | USTOL_EOS);
411                                         if (vp && opts)
412                                                 RELOPT(opts, x) = val - 1;
413                                 }
414                                 break;
415
416                         case 9:
417                                 /* Set syntax */
418                                 if (arg && opts)
419                                         opts->syntax_name = (unsigned char *)strdup((char *)arg);
420                                 break;
421
422                         case 13:
423                                 /* Set byte mode encoding */
424                                 if (arg && opts)
425                                         opts->map_name = (unsigned char *)strdup((char *)arg);
426                                 break;
427                         }
428                         /* This is a stupid hack... */
429                         if ((glopts[x].type & 3) == 0 || !arg)
430                                 return 1;
431                         else
432                                 return 2;
433                 }
434         /* Why no case 6, string option? */
435         /* Keymap, mold, mnew, etc. are not strings */
436         /* These options do not show up in ^T */
437         if (!strcmp(s, "lmsg")) {
438                 if (arg) {
439                         if (opts)
440                                 opts->lmsg = (unsigned char *)strdup((char *)arg);
441                         ret = 2;
442                 } else
443                         ret = 1;
444         } else if (!strcmp(s, "rmsg")) {
445                 if (arg) {
446                         if (opts)
447                                 opts->rmsg = (unsigned char *)strdup((char *)arg);
448                         ret = 2;
449                 } else
450                         ret = 1;
451         } else if (!strcmp(s, "hmsg")) {
452                 if (arg) {
453                         if (opts)
454                                 opts->hmsg = strdup((char *)arg);
455                         ret = 2;
456                 } else
457                         ret = 1;
458         } else if (!strcmp(s, "keymap")) {
459                 if (arg) {
460                         if (opts)
461                                 opts->context = (unsigned char *)strdup((char *)arg);
462                         ret = 2;
463                 } else
464                         ret = 1;
465         } else if (!strcmp(s, "mnew")) {
466                 if (arg) {
467                         int sta;
468
469                         if (opts)
470                                 opts->mnew = mparse(NULL, arg, &sta);
471                         ret = 2;
472                 } else
473                         ret = 1;
474         } else if (!strcmp(s, "mold")) {
475                 if (arg) {
476                         int sta;
477
478                         if (opts)
479                                 opts->mold = mparse(NULL, arg, &sta);
480                         ret = 2;
481                 } else
482                         ret = 1;
483         } else if (!strcmp(s, "msnew")) {
484                 if (arg) {
485                         int sta;
486
487                         if (opts)
488                                 opts->msnew = mparse(NULL, arg, &sta);
489                         ret = 2;
490                 } else
491                         ret = 1;
492         } else if (!strcmp(s, "msold")) {
493                 if (arg) {
494                         int sta;
495
496                         if (opts)
497                                 opts->msold = mparse(NULL, arg, &sta);
498                         ret = 2;
499                 } else
500                         ret = 1;
501         }
502
503         return ret;
504 }
505
506 /* Option setting user interface (^T command) */
507
508 /* Menu cursor position: remember it for next time */
509 static int optx = 0;
510
511 static int
512 doabrt1(BW *bw, int *xx)
513 {
514         free(xx);
515         return -1;
516 }
517
518 static int
519 doopt1(BW *bw, unsigned char *s, int *xx, int *notify)
520 {
521         int ret = 0;
522         int x = *xx;
523         long v;
524
525         free(xx);
526         switch (glopts[x].type) {
527         case 1:
528                 if (!*s) {
529                         ret = -1;
530                         break;
531                 }
532                 v = calcl(bw, s);
533                 if (merrf) {
534                         msgnw(bw->parent, merrt);
535                         ret = -1;
536                 } else if (v >= glopts[x].low && v <= glopts[x].high)
537                         *glopts[x].set.i = v;
538                 else {
539                         msgnw(bw->parent, UC "Value out of range");
540                         ret = -1;
541                 }
542                 break;
543         case 2:
544                 if (s[0])
545                         *glopts[x].set.us = (unsigned char *)strdup((char *)s);
546                 break;
547         case 5:
548                 if (!*s) {
549                         ret = -1;
550                         break;
551                 }
552                 v = calcl(bw, s);
553                 if (merrf) {
554                         msgnw(bw->parent, merrt);
555                         ret = -1;
556                 } else if (v >= glopts[x].low && v <= glopts[x].high)
557                         RELOPT(&bw->o, x) = v;
558                 else {
559                         msgnw(bw->parent, UC "Value out of range");
560                         ret = -1;
561                 }
562                 break;
563         case 7:
564                 if (!*s) {
565                         ret = -1;
566                         break;
567                 }
568                 v = calcl(bw, s) - 1L;
569                 if (merrf) {
570                         msgnw(bw->parent, merrt);
571                         ret = -1;
572                 } else if (v >= glopts[x].low && v <= glopts[x].high)
573                         RELOPT(&bw->o, x) = v;
574                 else {
575                         msgnw(bw->parent, UC "Value out of range");
576                         ret = -1;
577                 }
578                 break;
579         }
580         vsrm(s);
581         bw->b->o = bw->o;
582         wfit(bw->parent->t);
583         updall();
584         if (notify)
585                 *notify = 1;
586         return ret;
587 }
588
589 static int
590 dosyntax(BW *bw, unsigned char *s, int *xx, int *notify)
591 {
592         int ret = 0;
593         struct high_syntax *syn;
594
595         if (*s) {
596                 if ((syn = load_dfa(s)))
597                         bw->o.syntax = syn;
598                 else
599                         msgnw(bw->parent, UC "Syntax definition file not found");
600         } else
601                 bw->o.syntax = NULL;
602
603         vsrm(s);
604         bw->b->o = bw->o;
605         updall();
606         if (notify)
607                 *notify = 1;
608         return ret;
609 }
610
611 /* Array of available syntac─ôs */
612 unsigned char **syntaxes = NULL;
613
614 static int
615 syntaxcmplt(BW *bw)
616 {
617         if (!syntaxes) {
618                 unsigned char *oldpwd = pwd();
619                 unsigned char **t;
620                 unsigned char *p;
621                 int x, y;
622
623                 if (chJpwd(UC "syntax"))
624                         return -1;
625                 t = rexpnd(UC "*.jsf");
626                 if (!t) {
627                         chpwd(oldpwd);
628                         return -1;
629                 }
630                 if (!aLEN(t)) {
631                         varm(t);
632                         chpwd(oldpwd);
633                         return -1;
634                 }
635
636                 for (x = 0; x != aLEN(t); ++x) {
637                         unsigned char *r = vsncpy(NULL,0,t[x],(unsigned char *)strrchr((char *)(t[x]),'.')-t[x]);
638                         syntaxes = vaadd(syntaxes,r);
639                 }
640                 varm(t);
641
642                 p = (unsigned char *)getenv("HOME");
643                 if (p) {
644                         unsigned char buf[1024];
645                         joe_snprintf_1((char *)buf,sizeof(buf),"%s/.jupp/syntax",p);
646                         if (!chpwd(buf) && (t = rexpnd(UC "*.jsf"))) {
647                                 for (x = 0; x != aLEN(t); ++x)
648                                         *strrchr((char *)t[x],'.') = 0;
649                                 for (x = 0; x != aLEN(t); ++x) {
650                                         for (y = 0; y != aLEN(syntaxes); ++y)
651                                                 if (!strcmp(t[x],syntaxes[y]))
652                                                         break;
653                                         if (y == aLEN(syntaxes)) {
654                                                 unsigned char *r = vsncpy(NULL,0,sv(t[x]));
655                                                 syntaxes = vaadd(syntaxes,r);
656                                         }
657                                 }
658                                 varm(t);
659                         }
660                 }
661
662                 vasort(av(syntaxes));
663                 chpwd(oldpwd);
664         }
665         return simple_cmplt(bw,syntaxes);
666 }
667
668 static int
669 check_for_hex(BW *bw)
670 {
671         W *w;
672         if (bw->o.hex)
673                 return 1;
674         for (w = bw->parent->link.next; w != bw->parent; w = w->link.next)
675                 if ((w->watom == &watomtw || w->watom == &watompw) &&
676                     w->object.bw->b == bw->b && w->object.bw->o.hex)
677                         return 1;
678         return 0;
679 }
680
681 static int
682 doencoding(BW *bw, unsigned char *s, int *xx, int *notify)
683 {
684         int ret = 0;
685         struct charmap *map;
686
687         if (*s)
688                 map = find_charmap(s);
689         else
690                 map = fdefault.charmap;
691
692         if (map && map->type && check_for_hex(bw)) {
693                 vsrm(s);
694                 msgnw(bw->parent, UC "UTF-8 encoding not allowed with hex-edit windows");
695                 if (notify)
696                         *notify = 1;
697                 return -1;
698         }
699
700         if (map) {
701                 bw->o.charmap = map;
702                 joe_snprintf_1((char *)msgbuf, JOE_MSGBUFSIZE, "%s encoding assumed for this file", map->name);
703                 msgnw(bw->parent, msgbuf);
704         } else
705                 msgnw(bw->parent, UC "Character set not found");
706
707         vsrm(s);
708         bw->b->o = bw->o;
709         updall();
710         if (notify)
711                 *notify = 1;
712         return ret;
713 }
714
715 /* Array of available encodinges */
716 unsigned char **encodings = NULL;
717
718 static int
719 encodingcmplt(BW *bw)
720 {
721         if (!encodings) {
722                 encodings = get_encodings();
723                 vasort(av(encodings));
724         }
725         return simple_cmplt(bw,encodings);
726 }
727
728 static int
729 doopt(MENU *m, int x, void *object, int flg)
730 {
731         BW *bw = m->parent->win->object.bw;
732         int *xx;
733         unsigned char buf[OPT_BUF_SIZE];
734         int *notify = m->parent->notify;
735
736         switch (glopts[x].type) {
737         case 0:
738                 if (!flg)
739                         *glopts[x].set.i = !*glopts[x].set.i;
740                 else if (flg == 1)
741                         *glopts[x].set.i = 1;
742                 else
743                         *glopts[x].set.i = 0;
744                 wabort(m->parent);
745                 msgnw(bw->parent, *glopts[x].set.i ? glopts[x].yes : glopts[x].no);
746                 if (glopts[x].set.i == &noxon)
747                         tty_xonoffbaudrst();
748                 break;
749         case 4:
750                 if (!flg)
751                         RELOPT(&bw->o, x) = !RELOPT(&bw->o, x);
752                 else if (flg == 1)
753                         RELOPT(&bw->o, x) = 1;
754                 else
755                         RELOPT(&bw->o, x) = 0;
756                 wabort(m->parent);
757                 msgnw(bw->parent, RELOPT(&bw->o, x) ? glopts[x].yes : glopts[x].no);
758                 /*XXX use offsetof, also in izopts or better statically */
759                 if (glopts[x].ofst == (unsigned char *) &fdefault.readonly - (unsigned char *) &fdefault)
760                         bw->b->rdonly = bw->o.readonly;
761                 /* Kill UTF-8 and CR-LF mode if we switch to hex display */
762                 if (glopts[x].ofst == (unsigned char *)&fdefault.hex - (unsigned char *)&fdefault &&
763                     bw->o.hex) {
764                         if (bw->b->o.charmap->type) {
765                                 doencoding(bw, vsncpy(NULL, 0, sc("c")),
766                                     NULL, NULL);
767                         }
768                         bw->o.crlf = 0;
769                 }
770                 break;
771         case 1:
772                 joe_snprintf_1((char *)buf, OPT_BUF_SIZE, (char *)glopts[x].yes, *glopts[x].set.i);
773                 xx = malloc(sizeof(int));
774
775                 *xx = x;
776                 m->parent->notify = 0;
777                 wabort(m->parent);
778                 if (wmkpw(bw->parent, buf, NULL, doopt1, NULL, doabrt1, utypebw, xx, notify, locale_map)) {
779                         if (glopts[x].set.i == &Baud)
780                                 tty_xonoffbaudrst();
781                         return 0;
782                 } else
783                         return -1;
784         case 2:
785                 if (*glopts[x].set.us)
786                         joe_snprintf_1((char *)buf, OPT_BUF_SIZE, (char *)glopts[x].yes, *glopts[x].set.us);
787                 else
788                         joe_snprintf_1((char *)buf, OPT_BUF_SIZE, (char *)glopts[x].yes, "");
789                 xx = malloc(sizeof(int));
790
791                 *xx = x;
792                 m->parent->notify = 0;
793                 wabort(m->parent);
794                 if (wmkpw(bw->parent, buf, NULL, doopt1, NULL, doabrt1, utypebw, xx, notify, locale_map))
795                         return 0;
796                 else
797                         return -1;
798         case 5:
799                 joe_snprintf_1((char *)buf, OPT_BUF_SIZE,
800                     (const char *)glopts[x].yes, RELOPT(&bw->o, x));
801                 goto in;
802         case 7:
803                 joe_snprintf_1((char *)buf, OPT_BUF_SIZE,
804                     (const char *)glopts[x].yes, RELOPT(&bw->o, x) + 1);
805  in:
806                 xx = malloc(sizeof(int));
807
808                 *xx = x;
809                 m->parent->notify = 0;
810                 wabort(m->parent);
811                 if (wmkpw(bw->parent, buf, NULL, doopt1, NULL, doabrt1, utypebw, xx, notify, locale_map))
812                         return 0;
813                 else
814                         return -1;
815
816         case 9:
817                 joe_snprintf_1((char *)buf, OPT_BUF_SIZE, (char *)glopts[x].yes,
818                     bw->b->o.syntax ? bw->b->o.syntax->name : UC "(unset)");
819                 m->parent->notify = 0;
820                 wabort(m->parent);
821                 if (wmkpw(bw->parent, buf, NULL, dosyntax, NULL, NULL, syntaxcmplt, NULL, notify, locale_map))
822                         return 0;
823                 else
824                         return -1;
825
826         case 13:
827                 joe_snprintf_1((char *)buf, OPT_BUF_SIZE, (char *)glopts[x].yes,
828                     bw->b->o.charmap ? bw->b->o.charmap->name : UC "(unset)");
829                 m->parent->notify = 0;
830                 wabort(m->parent);
831                 if (wmkpw(bw->parent, buf, NULL, doencoding, NULL, NULL, encodingcmplt, NULL, notify, locale_map))
832                         return 0;
833                 else
834                         return -1;
835         }
836         if (notify)
837                 *notify = 1;
838         bw->b->o = bw->o;
839         wfit(bw->parent->t);
840         updall();
841         return 0;
842 }
843
844 static int
845 doabrt(MENU *m, int x, unsigned char **s)
846 {
847         optx = x;
848         for (x = 0; s[x]; ++x)
849                 free(s[x]);
850         free(s);
851         return -1;
852 }
853
854 int
855 umode(BW *bw)
856 {
857         size_t size = 0, x, len;
858         unsigned char **s;
859
860         bw->b->o.readonly = bw->o.readonly = bw->b->rdonly;
861         while (glopts[size].menu)
862                 ++size;
863         s = ralloc(size + 1, sizeof(unsigned char *));
864         len = 0;
865         for (x = 0; x < size; ++x) {
866                 s[x] = malloc(OPT_BUF_SIZE);
867                 if (glopts[x].menu[0] == ' ' || glopts[x].menu[1] == ' ')
868                         strlcpy(s[x], glopts[x].menu, OPT_BUF_SIZE);
869                 else {
870                         strlcpy(s[x] + 2, glopts[x].menu, OPT_BUF_SIZE);
871                         s[x][0] = s[x][2];
872                         s[x][1] = ' ';
873                 }
874                 if (strlen(s[x]) > len)
875                         len = strlen(s[x]);
876         }
877         for (x = 0; x < size; ++x) {
878                 size_t n = strlen(s[x]);
879
880                 while (len - n)
881                         s[x][n++] = ' ';
882                 switch (glopts[x].type) {
883                 case 0:
884                         joe_snprintf_1(s[x] + n, OPT_BUF_SIZE - n,
885                             "%s", *glopts[x].set.i ? "ON" : "OFF");
886                         break;
887                 case 1:
888                         joe_snprintf_1(s[x] + n, OPT_BUF_SIZE - n,
889                             "%d", *glopts[x].set.i);
890                         break;
891                 case 2:
892                         strlcpy(s[x] + n, "...", OPT_BUF_SIZE - n);
893                         break;
894                 case 4:
895                         joe_snprintf_1(s[x] + n, OPT_BUF_SIZE - n,
896                             "%s", RELOPT(&bw->o, x) ? "ON" : "OFF");
897                         break;
898                 case 5:
899                         joe_snprintf_1(s[x] + n, OPT_BUF_SIZE - n,
900                             "%d", RELOPT(&bw->o, x));
901                         break;
902                 case 7:
903                         joe_snprintf_1(s[x] + n, OPT_BUF_SIZE - n,
904                             "%d", RELOPT(&bw->o, x) + 1);
905                         break;
906                 case 9:
907                         /* XXX aligns differently so it doesn't get too large */
908                         joe_snprintf_2(s[x] + 12, OPT_BUF_SIZE - 12, "%*s", (int)n - 9,
909                             bw->b->o.syntax ? bw->b->o.syntax->name : UC "(unset)");
910                         break;
911                 case 13:
912                         /* XXX aligns differently so it doesn't get too large */
913                         joe_snprintf_2(s[x] + 12, OPT_BUF_SIZE - 12, "%*s", (int)n - 9,
914                             bw->b->o.charmap ? bw->b->o.charmap->name : UC "(unset)");
915                         break;
916                 default:
917                         s[x][n] = '\0';
918                 }
919         }
920         s[x] = NULL;
921         if (mkmenu(bw->parent, s, doopt, doabrt, NULL, optx, s, NULL))
922                 return 0;
923         else
924                 return -1;
925 }
926
927 /*-
928  * Process rc file
929  * Returns 0 if the rc file was succefully processed
930  *        -1 if the rc file couldn't be opened
931  *         1 if there was a syntax error in the file
932  */
933 int
934 procrc(CAP *cap, const unsigned char *name)
935 {
936         OPTIONS *o = &fdefault; /* Current options */
937         KMAP *context = NULL;   /* Current context */
938         unsigned char buf[1024];/* Input buffer */
939         JFILE *fd;              /* rc file */
940         int line = 0;           /* Line number */
941         int err = 0;            /* Set to 1 if there was a syntax error */
942         int x, c, y, sta;
943         unsigned char *opt, *arg;
944         MACRO *m;
945
946         if (!(fd = jfopen(name, "r")))
947                 /* return if we couldn't open the rc file */
948                 return (-1);
949
950         fprintf(stderr, "Processing '%s'...", name);
951         fflush(stderr);
952
953         while (jfgets(buf, sizeof(buf), fd)) {
954                 line++;
955                 switch (buf[0]) {
956                 case ' ':
957                 case '\t':
958                 case '\n':
959                 case '\f':
960                 case 0:
961                         /* skip comment lines */
962                         break;
963                 case '*':
964                         /* select file types for file type-dependent options */
965                         o = malloc(sizeof(OPTIONS));
966                         *o = fdefault;
967                         x = 0;
968                         while (buf[x] && buf[x] != '\n' && buf[x] != ' ' && buf[x] != '\t')
969                                 ++x;
970                         buf[x] = 0;
971                         o->next = options;
972                         options = o;
973                         o->name_regex = (unsigned char *)strdup((char *)buf);
974                         break;
975                 case '+':
976                         /* Set file contents match regex */
977                         x = 0;
978                         while (buf[x] && buf[x] != '\n' && buf[x] != '\r')
979                                 ++x;
980                         buf[x] = 0;
981                         if (o)
982                                 o->contents_regex = (unsigned char *)strdup((char *)(buf+1));
983                         break;
984                 case '-':
985                         /* Set an option */
986                         opt = buf + 1;
987                         arg = NULL;
988                         x = 0;
989                         while (buf[x] && buf[x] != '\n' && buf[x] != ' ' && buf[x] != '\t')
990                                 ++x;
991                         if (buf[x] && buf[x] != '\n') {
992                                 buf[x] = 0;
993                                 arg = buf + ++x;
994                                 while (buf[x] && buf[x] != '\n')
995                                         ++x;
996                         }
997                         buf[x] = 0;
998                         if (!glopt(opt, arg, o, 2)) {
999                                 err = 1;
1000                                 fprintf(stderr, "\n%s:%d: Unknown option '%s'", name, line, opt);
1001                         }
1002                         break;
1003                 case '{':
1004                         /* Ignore help text */
1005                         while (jfgets(buf, sizeof(buf), fd) && buf[0] != /*{*/'}')
1006                                 /* do nothing */;
1007                         if (buf[0] != '}') {
1008                                 err = 1;
1009                                 fprintf(stderr, "\n%s:%d: End of rc file occurred before end of help text\n", name, line);
1010                                 break;
1011                         }
1012                         break;
1013                 case ':':
1014                         /* Select context */
1015                         x = 1;
1016                         while (!joe_isspace_eof(locale_map,buf[x]))
1017                                 ++x;
1018                         c = buf[x];
1019                         buf[x] = 0;
1020                         if (x != 1)
1021                                 if (!strcmp(buf + 1, "def")) {
1022                                         for (buf[x] = c; joe_isblank(locale_map,buf[x]); ++x) ;
1023                                         for (y = x; !joe_isspace_eof(locale_map,buf[y]); ++y) ;
1024                                         c = buf[y];
1025                                         buf[y] = 0;
1026                                         if (y == x) {
1027                                                 err = 1;
1028                                                 fprintf(stderr, "\n%s:%d: command name missing from :def", name, line);
1029                                         } else if (joe_isblank(locale_map, c) &&
1030                                             (m = mparse(NULL, buf + y + 1, &sta)))
1031                                                 addcmd(buf + x, m);
1032                                         else {
1033                                                 err = 1;
1034                                                 fprintf(stderr, "\n%s:%d: macro missing from :def", name, line);
1035                                         }
1036                                 } else if (!strcmp(buf + 1, "inherit"))
1037                                         if (context) {
1038                                                 for (buf[x] = c; joe_isblank(locale_map,buf[x]); ++x) ;
1039                                                 for (c = x; !joe_isspace_eof(locale_map,buf[c]); ++c) ;
1040                                                 buf[c] = 0;
1041                                                 if (c != x)
1042                                                         kcpy(context, kmap_getcontext(buf + x, 1));
1043                                                 else {
1044                                                         err = 1;
1045                                                         fprintf(stderr, "\n%s:%d: context name missing from :inherit", name, line);
1046                                                 }
1047                                         } else {
1048                                                 err = 1;
1049                                                 fprintf(stderr, "\n%s:%d: No context selected for :inherit", name, line);
1050                                 } else if (!strcmp(buf + 1, "include")) {
1051                                         for (buf[x] = c; joe_isblank(locale_map,buf[x]); ++x) ;
1052                                         for (c = x; !joe_isspace_eof(locale_map,buf[c]); ++c) ;
1053                                         buf[c] = 0;
1054                                         if (c != x) {
1055                                                 switch (procrc(cap, buf + x)) {
1056                                                 case 1:
1057                                                         err = 1;
1058                                                         break;
1059                                                 case -1:
1060                                                         fprintf(stderr, "\n%s:%d: Couldn't open %s", name, line, buf + x);
1061                                                         err = 1;
1062                                                         break;
1063                                                 }
1064                                                 context = 0;
1065                                                 o = &fdefault;
1066                                         } else {
1067                                                 err = 1;
1068                                                 fprintf(stderr, "\n%s:%d: :include missing file name", name, line);
1069                                         }
1070                                 } else if (!strcmp(buf + 1, "delete"))
1071                                         if (context) {
1072                                                 for (buf[x] = c; joe_isblank(locale_map,buf[x]); ++x) ;
1073                                                 for (y = x; buf[y] != 0 && buf[y] != '\t' && buf[y] != '\n' && (buf[y] != ' ' || buf[y + 1]
1074                                                                                                                 != ' '); ++y) ;
1075                                                 buf[y] = 0;
1076                                                 kdel(context, buf + x);
1077                                         } else {
1078                                                 err = 1;
1079                                                 fprintf(stderr, "\n%s:%d: No context selected for :delete", name, line);
1080                                 } else
1081                                         context = kmap_getcontext(buf + 1, 1);
1082                         else {
1083                                 err = 1;
1084                                 fprintf(stderr, "\n%s:%d: Invalid context name", name, line);
1085                         }
1086                         break;
1087                 default:
1088                         /* Get key-sequence to macro binding */
1089                         if (!context) {
1090                                 err = 1;
1091                                 fprintf(stderr, "\n%s:%d: No context selected for macro to key-sequence binding", name, line);
1092                                 break;
1093                         }
1094
1095                         m = NULL;
1096  macroloop:
1097                         m = mparse(m, buf, &x);
1098                         if (x == -1) {
1099                                 err = 1;
1100                                 fprintf(stderr, "\n%s:%d: Unknown command in macro", name, line);
1101                                 break;
1102                         } else if (x == -2) {
1103                                 jfgets(buf, sizeof(buf), fd);
1104                                 goto macroloop;
1105                         }
1106                         if (!m)
1107                                 break;
1108
1109                         /* Skip to end of key sequence */
1110                         for (y = x; buf[y] != 0 && buf[y] != '\t' && buf[y] != '\n' && (buf[y] != ' ' || buf[y + 1] != ' '); ++y) ;
1111                         buf[y] = 0;
1112
1113                         /* Add binding to context */
1114                         if (kadd(cap, context, buf + x, m) == -1) {
1115                                 fprintf(stderr, "\n%s:%d: Bad key sequence '%s'", name, line, buf + x);
1116                                 err = 1;
1117                         }
1118                         break;
1119                 }
1120         }
1121         /* close rc file */
1122         jfclose(fd);
1123
1124         /* Print proper ending string */
1125         fprintf(stderr, "%cdone\n", err ? '\n' : ' ');
1126
1127         /* 0 for success, 1 for syntax error */
1128         return (err);
1129 }