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