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