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