1 /*
2  *	modes.c
3  *
4  * Maintain and list the editor modes and value sets.
5  *
6  * Original code probably by Dan Lawrence or Dave Conroy for MicroEMACS.
7  * Major extensions for vile by Paul Fox, 1991
8  * Majormode extensions for vile by T.E.Dickey, 1997
9  *
10  * $Id: modes.c,v 1.458 2020/08/23 19:03:03 Stephan.Schulz Exp $
11  */
12 
13 #include <estruct.h>
14 #include <edef.h>
15 #include <blist.h>
16 #include <chgdfunc.h>
17 #include <nefsms.h>
18 
19 #define	ONE_COL	(80/3)
20 
21 #define isLocalVal(valptr)          ((valptr)->vp == &((valptr)->v))
22 #define makeLocalVal(valptr)        ((valptr)->vp = &((valptr)->v))
23 
24 #define MAJOR_SUFFIX	"mode"
25 #define NO_PREFIX	"no"
26 #define noPrefix(mode)	(!strncmp(mode, NO_PREFIX, (size_t) 2))
27 
28 /*--------------------------------------------------------------------------*/
29 
30 #if OPT_UPBUFF
31 static void relist_settings(void);
32 #else
33 #define relist_settings()	/* nothing */
34 #endif
35 
36 #if OPT_ENUM_MODES
37 static int fsm_idx;
38 static FSM_BLIST *valname_to_choices(const struct VALNAMES *names);
39 #endif
40 
41 /*--------------------------------------------------------------------------*/
42 
43 #if OPT_EVAL || OPT_MAJORMODE
44 static size_t size_my_varmodes;
45 static char **my_varmodes;	/* list for modename-completion */
46 #endif
47 
48 #if OPT_MAJORMODE
49 
50 #define mmShortName(n) my_majormodes[majormodes_order[n]].shortname
51 
52 typedef struct {
53     char *shortname;		/* copy of MAJORMODE.shortname */
54     char *longname;		/* copy of MAJORMODE.longname */
55     MAJORMODE *data;		/* pointer to actual data */
56     int init;			/* predefined during initialization */
57     int flag;			/* true when majormode is active/usable */
58     struct VALNAMES qual[MAX_M_VALUES + 1];	/* majormode qualifier names */
59     struct VALNAMES used[MAX_B_VALUES + 1];	/* submode names */
60     struct VALNAMES subq[MAX_Q_VALUES + 1];	/* submode qualifier names */
61 } MAJORMODE_LIST;
62 
63 static MAJORMODE_LIST *my_majormodes;
64 static MAJORMODE_LIST no_majormodes[1];
65 static BLIST majormode_blist = init_blist(no_majormodes);
66 
67 static int *majormodes_order;	/* index, for precedence */
68 static M_VALUES global_m_values;	/* dummy, for convenience */
69 static struct VAL *major_g_vals;	/* on/off values of major modes */
70 static struct VAL *major_l_vals;	/* dummy, for convenience */
71 static struct VALNAMES *major_valnames;
72 
73 static int did_attach_mmode;
74 
75 static const char **my_mode_list = 0;	/* copy of 'all_modes[]' */
76 
77 #define is_bool_type(type) ((type) == VALTYPE_BOOL || (type) == VALTYPE_MAJOR)
78 
79 static MAJORMODE_LIST *lookup_mm_list(const char *name);
80 static char *majorname(char *dst, const char *majr, int flag);
81 static const char *ModeName(const char *name);
82 static int attach_mmode(BUFFER *bp, const char *name);
83 static int detach_mmode(BUFFER *bp, const char *name);
84 static int enable_mmode(const char *name, int flag);
85 static struct VAL *get_sm_vals(MAJORMODE * ptr);
86 static void init_my_mode_list(void);
87 
88 #if OPT_UPBUFF
89 static void relist_majormodes(void);
90 #endif
91 
92 #else
93 
94 #define ModeName(s) s
95 #define init_my_mode_list()	/* nothing */
96 #define is_bool_type(type) ((type) == VALTYPE_BOOL)
97 #define my_mode_list all_modes
98 #define relist_majormodes()	/* nothing */
99 
100 #endif /* OPT_MAJORMODE */
101 
102 #define is_int_type(type) ((type) == VALTYPE_INT)
103 #define is_str_type(type) ((type) == VALTYPE_REGEX || (type) == VALTYPE_STRING)
104 
105 static BLIST blist_my_mode_list = init_blist(all_modes);
106 
107 /*--------------------------------------------------------------------------*/
108 
109 #if OPT_ENUM_MODES || !SMALLER
110 int
choice_to_code(FSM_BLIST * data,const char * name,size_t len)111 choice_to_code(FSM_BLIST * data, const char *name, size_t len)
112 {
113     const FSM_CHOICES *choices = data->choices;
114     char temp[NSTRING];
115     int code = ENUM_ILLEGAL;
116     int i;
117 
118     if (choices != 0 && name != 0 && len != 0) {
119 	if (len > NSTRING - 1)
120 	    len = NSTRING - 1;
121 
122 	memcpy(temp, name, len);
123 	temp[len] = EOS;
124 	(void) mklower(temp);
125 
126 	if ((i = blist_pmatch(&(data->blist), temp, (int) len)) >= 0) {
127 	    code = choices[i].choice_code;
128 	}
129     }
130     return code;
131 }
132 
133 const char *
choice_to_name(FSM_BLIST * data,int code)134 choice_to_name(FSM_BLIST * data, int code)
135 {
136     const char *name = "";
137     int i;
138 
139     if (data != 0) {
140 	const FSM_CHOICES *choices = data->choices;
141 	if (choices != 0) {
142 	    for (i = 0; choices[i].choice_name != 0; i++) {
143 		if (choices[i].choice_code == code) {
144 		    name = choices[i].choice_name;
145 		    break;
146 		}
147 	    }
148 	}
149     }
150     return name;
151 }
152 #endif /* OPT_ENUM_MODES || !SMALLER */
153 
154 #if !SMALLER
155 /*
156  * Add the values from one or more keywords.  If any error is found, return that
157  * code instead.
158  */
159 int
combine_choices(FSM_BLIST * choices,const char * string)160 combine_choices(FSM_BLIST * choices, const char *string)
161 {
162     char temp[NSTRING];
163     const char *s;
164     int code;
165     int result = 0;
166 
167     while (*string) {
168 	vl_strncpy(temp, string, sizeof(temp));
169 	if ((s = strchr(string, '+')) != 0) {
170 	    temp[s - string] = EOS;
171 	    string = s + 1;
172 	} else {
173 	    string += strlen(string);
174 	}
175 	if ((code = choice_to_code(choices, temp, strlen(temp))) >= 0) {
176 	    result += code;
177 	} else {
178 	    result = code;
179 	    break;
180 	}
181     }
182     return result;
183 }
184 #endif
185 
186 /*--------------------------------------------------------------------------*/
187 
188 /*
189  * Update window-flags applying to the current buffer when we modify a window
190  * property, or something that requires repainting a window.
191  */
192 void
set_bufflags(int glob_vals,unsigned flags)193 set_bufflags(int glob_vals, unsigned flags)
194 {
195     if (glob_vals) {
196 	set_winflags(glob_vals, flags);
197     } else {
198 	WINDOW *wp;
199 	for_each_visible_window(wp) {
200 	    if (wp->w_bufp == curbp)
201 		wp->w_flag |= (USHORT) flags;
202 	}
203     }
204 }
205 
206 /*
207  * Update window-flags when we modify a window property, or something that
208  * requires repainting all (or the current if local) windows.
209  */
210 void
set_winflags(int glob_vals,unsigned flags)211 set_winflags(int glob_vals, unsigned flags)
212 {
213     if (glob_vals) {
214 	WINDOW *wp;
215 	for_each_visible_window(wp) {
216 	    if ((wp->w_bufp == NULL)
217 		|| !b_is_scratch(wp->w_bufp)
218 		|| !(flags & WFMODE))
219 		wp->w_flag |= (USHORT) flags;
220 	}
221     } else {
222 	curwp->w_flag |= (USHORT) flags;
223     }
224 }
225 
226 static int
same_val(const struct VALNAMES * names,struct VAL * tst,struct VAL * ref)227 same_val(const struct VALNAMES *names, struct VAL *tst, struct VAL *ref)
228 {
229     if (ref == 0)		/* can't test, not really true */
230 	return -TRUE;
231 
232     switch (names->type) {
233 #if OPT_MAJORMODE
234     case VALTYPE_MAJOR:
235 	/*FALLTHRU */
236 #endif
237     case VALTYPE_BOOL:
238     case VALTYPE_ENUM:
239     case VALTYPE_INT:
240 	return (tst->vp->i == ref->vp->i);
241     case VALTYPE_STRING:
242 	return (tst->vp->p != 0)
243 	    && (ref->vp->p != 0)
244 	    && !strcmp(tst->vp->p, ref->vp->p);
245     case VALTYPE_REGEX:
246 	return (tst->vp->r != 0)
247 	    && (ref->vp->r != 0)
248 	    && (tst->vp->r->pat != 0)
249 	    && (ref->vp->r->pat != 0)
250 	    && !strcmp(tst->vp->r->pat, ref->vp->r->pat);
251     default:
252 	mlforce("BUG: bad type %s %d", ModeName(names->name), names->type);
253     }
254 
255     return FALSE;
256 }
257 
258 /*
259  * Returns the value if it is a string, null otherwise.
260  */
261 static const char *
string_val(const struct VALNAMES * names,struct VAL * values)262 string_val(const struct VALNAMES *names, struct VAL *values)
263 {
264     const char *s = 0;
265 
266     switch (names->type) {
267 #if OPT_ENUM_MODES		/* will show the enum name too */
268     case VALTYPE_ENUM:
269 	s = choice_to_name(valname_to_choices(names), values->vp->i);
270 	break;
271 #endif
272     case VALTYPE_STRING:
273 	s = values->vp->p;
274 	break;
275     case VALTYPE_REGEX:
276 	if (values->vp->r)
277 	    s = values->vp->r->pat;
278 	break;
279     }
280 
281     return isEmpty(s) ? 0 : s;
282 }
283 
284 /*
285  * Returns the formatted length of a string value.
286  */
287 static size_t
size_val(const struct VALNAMES * names,struct VAL * values)288 size_val(const struct VALNAMES *names, struct VAL *values)
289 {
290     return strlen(ModeName(names->name))
291 	+ 3
292 	+ strlen(NonNull(string_val(names, values)));
293 }
294 
295 /*
296  * Returns a mode-value formatted as a string
297  */
298 const char *
string_mode_val(VALARGS * args)299 string_mode_val(VALARGS * args)
300 {
301     struct VAL *values = args->local;
302     const char *result = error_val;
303 
304     if (values != 0) {
305 	const struct VALNAMES *names = args->names;
306 	union V *actual = values->vp ? values->vp : &(values->v);
307 	static TBUFF *buffer;
308 
309 	switch (names->type) {
310 #if OPT_MAJORMODE
311 	case VALTYPE_MAJOR:
312 #endif
313 	case VALTYPE_BOOL:
314 	    result = actual->i ? "TRUE" : "FALSE";
315 	    break;
316 	case VALTYPE_ENUM:
317 #if OPT_ENUM_MODES
318 	    {
319 		static TBUFF *temp = 0;		/* const workaround */
320 		(void) tb_scopy(&temp,
321 				choice_to_name(valname_to_choices(names),
322 					       actual->i));
323 		result = tb_values(temp);
324 		break;
325 	    }
326 #endif /* else, fall-thru to use int-code */
327 	case VALTYPE_INT:
328 	    result = render_int(&buffer, actual->i);
329 	    break;
330 	case VALTYPE_STRING:
331 	    result = NonNull(actual->p);
332 	    break;
333 	case VALTYPE_REGEX:
334 	    if (actual->r != 0)
335 		result = NonNull(actual->r->pat);
336 	    break;
337 	}
338     }
339     return result;
340 }
341 
342 /* listvalueset:  print each value in the array according to type, along with
343  * its name, until a NULL name is encountered.  If not local, only print if the value in the
344  * two arrays differs, or the second array is nil.  If local, print only the
345  * values in the first array that are local.
346  */
347 static int
listvalueset(const char * which,int nflag,int local,const struct VALNAMES * names,struct VAL * values,struct VAL * globvalues)348 listvalueset(const char *which,
349 	     int nflag,
350 	     int local,
351 	     const struct VALNAMES *names,
352 	     struct VAL *values,
353 	     struct VAL *globvalues)
354 {
355     int show[MAX_G_VALUES + MAX_B_VALUES + MAX_W_VALUES];
356     int any = 0;
357     int passes = 1;
358     int ncols = term.cols / ONE_COL;
359     int padded;
360     int perline;
361     int percol;
362     int total;
363     int j, pass;
364 
365     if (ncols > MAXCOLS)
366 	ncols = MAXCOLS;
367 
368     /*
369      * First, make a list of values we want to show.
370      * Store:
371      *      0 - don't show
372      *      1 - show in first pass
373      *      2 - show in second pass (too long)
374      */
375     for (j = 0; names[j].name != 0; j++) {
376 	int ok;
377 	show[j] = 0;
378 	if (local) {
379 	    ok = is_local_val(values, j);
380 	} else {
381 	    ok = (same_val(names + j, values + j,
382 			   globvalues
383 			   ? globvalues + j
384 			   : 0) != TRUE);
385 	}
386 	if (ok) {
387 	    switch (names[j].type) {
388 	    case VALTYPE_ENUM:
389 	    case VALTYPE_STRING:
390 	    case VALTYPE_REGEX:
391 		if (string_val(names + j, values + j) == 0) {
392 		    ok = FALSE;
393 		    break;
394 		}
395 		if (size_val(names + j, values + j) >= ONE_COL) {
396 		    show[j] += 1;
397 		    passes = 2;
398 		}
399 		/* fall-thru */
400 	    default:
401 		show[j] += 1;
402 	    }
403 	}
404 	if (ok && !any++) {
405 	    if (nflag)
406 		bputc('\n');
407 	    bprintf("%s:\n", which);
408 	}
409     }
410     total = j;
411 
412     if (any) {
413 	if (!passes)
414 	    passes = 1;
415     } else
416 	return nflag;
417 
418     /*
419      * Now, go back and display the values
420      */
421     for (pass = 1; pass <= passes; pass++) {
422 	int line, col, k;
423 	int offsets[MAXCOLS + 1];
424 
425 	offsets[0] = 0;
426 	if (pass == 1) {
427 	    for (j = percol = 0; j < total; j++) {
428 		if (show[j] == pass)
429 		    percol++;
430 	    }
431 	    for (j = 1; j < ncols; j++) {
432 		offsets[j]
433 		    = (percol + ncols - j) / ncols
434 		    + offsets[j - 1];
435 	    }
436 	    perline = ncols;
437 	} else {		/* these are too wide for ONE_COL */
438 	    offsets[1] = total;
439 	    perline = 1;
440 	}
441 	offsets[ncols] = total;
442 
443 	line = 0;
444 	col = 0;
445 	any = 0;
446 	for_ever {
447 	    k = line + offsets[col];
448 	    for (j = 0; j < total; j++) {
449 		if (show[j] == pass) {
450 		    if (k-- <= 0)
451 			break;
452 		}
453 	    }
454 	    if (k >= 0)		/* no more cells to display */
455 		break;
456 
457 	    if (col == 0)
458 		bputc(' ');
459 	    padded = DOT.o + ((col + 1) < perline ? ONE_COL : 1);
460 	    if (is_bool_type(names[j].type)) {
461 		bprintf("%s%s",
462 			values[j].vp->i ? "  " : NO_PREFIX,
463 			ModeName(names[j].name));
464 	    } else {
465 		VALARGS args;
466 		args.names = names + j;
467 		args.local = values + j;
468 		args.global = 0;
469 		bprintf("  %s=", ModeName(names[j].name));
470 		bputsn_xcolor(string_mode_val(&args), -1,
471 			      (names[j].type == VALTYPE_ENUM)
472 			      ? XCOLOR_ENUM
473 			      : ((names[j].type == VALTYPE_REGEX)
474 				 ? XCOLOR_REGEX
475 				 : (is_str_type(names[j].type)
476 				    ? XCOLOR_STRING
477 				    : (is_int_type(names[j].type)
478 				       ? XCOLOR_NUMBER
479 				       : XCOLOR_NONE))));
480 	    }
481 	    bpadc(' ', padded - DOT.o);
482 	    any++;
483 	    if (++col >= perline) {
484 		col = 0;
485 		bputc('\n');
486 		if (++line >= offsets[1])
487 		    break;
488 	    } else if (line + offsets[col] >= offsets[col + 1])
489 		break;
490 	}
491 	if (any && (pass != passes))
492 	    bputc('\n');
493 	if (col != 0)
494 	    bputc('\n');
495     }
496     return TRUE;
497 }
498 
499 #ifdef lint
500 static /*ARGSUSED */ WINDOW *
ptr2WINDOW(void * p)501 ptr2WINDOW(void *p)
502 {
503     return 0;
504 }
505 #else
506 #define	ptr2WINDOW(p)	(WINDOW *)p
507 #endif
508 
509 /* list the current modes into the current buffer */
510 /* ARGSUSED */
511 static void
makemodelist(int local,void * ptr)512 makemodelist(int local, void *ptr)
513 {
514     static const char gg[] = "Universal";
515     static const char bb[] = "Buffer";
516     static const char ww[] = "Window";
517     int nflag, nflg2;
518 
519 #if OPT_ENUM_MODES
520     int save_fsm_idx = fsm_idx;
521 #endif
522     WINDOW *localwp = ptr2WINDOW(ptr);	/* alignment okay */
523     BUFFER *localbp = localwp->w_bufp;
524     struct VAL *local_b_vals = localbp->b_values.bv;
525     struct VAL *local_w_vals = localwp->w_values.wv;
526     struct VAL *globl_b_vals = global_b_values.bv;
527 
528 #if OPT_UPBUFF
529     if (relisting_b_vals != 0)
530 	local_b_vals = relisting_b_vals;
531     if (relisting_w_vals != 0)
532 	local_w_vals = relisting_w_vals;
533 #endif
534 
535 #if OPT_MAJORMODE
536     if (local && (localbp->majr != 0)) {
537 	bprintf("--- \"%s\" settings, if different than \"%s\" majormode ",
538 		localbp->b_bname,
539 		localbp->majr->shortname);
540 	globl_b_vals = get_sm_vals(localbp->majr);
541     } else
542 #endif
543 	bprintf("--- \"%s\" settings, if different than globals ",
544 		localbp->b_bname);
545     bpadc('-', term.cols - DOT.o);
546     bputc('\n');
547 
548     nflag = listvalueset(bb, FALSE, FALSE, b_valnames, local_b_vals, globl_b_vals);
549     nflg2 = listvalueset(ww, nflag, FALSE, w_valnames, local_w_vals, global_w_values.wv);
550     if (!(nflag || nflg2))
551 	bputc('\n');
552     bputc('\n');
553 
554     bprintf("--- %s settings ", local ? "Local" : "Global");
555     bpadc('-', term.cols - DOT.o);
556     bputc('\n');
557 
558     if (local) {
559 	nflag = listvalueset(bb, nflag, local, b_valnames, local_b_vals,
560 			     (struct VAL *) 0);
561 	(void) listvalueset(ww, nflag, local, w_valnames, local_w_vals,
562 			    (struct VAL *) 0);
563     } else {
564 	nflag = listvalueset(gg, nflag, local, g_valnames,
565 			     global_g_values.gv, (struct VAL *) 0);
566 	nflag = listvalueset(bb, nflag, local, b_valnames, globl_b_vals,
567 			     (struct VAL *) 0);
568 	(void) listvalueset(ww, nflag, local, w_valnames,
569 			    global_w_values.wv, (struct VAL *) 0);
570     }
571 #if OPT_ENUM_MODES
572     fsm_idx = save_fsm_idx;
573 #endif
574 }
575 
576 /*
577  * Set tab size
578  */
579 int
settab(int f,int n)580 settab(int f, int n)
581 {
582     WINDOW *wp;
583 #if OPT_MAJORMODE
584     int val = VAL_TAB;
585     const char *whichtabs = "T";
586 #else
587     int val;
588     const char *whichtabs;
589     if (is_c_mode(curbp)) {
590 	val = VAL_C_TAB;
591 	whichtabs = "C-t";
592     } else {
593 	val = VAL_TAB;
594 	whichtabs = "T";
595     }
596 #endif
597     if (f && n >= 1) {
598 	set_local_b_val(curbp, val, n);
599 	for_each_visible_window(wp) {
600 	    if (wp->w_bufp == curbp)
601 		wp->w_flag |= WFHARD;
602 	}
603     } else if (f) {
604 	mlwarn("[Illegal tabstop value]");
605 	return FALSE;
606     }
607     if (!global_b_val(MDTERSE) || !f)
608 	mlwrite("[%sabs are %d columns apart, using %s value.]", whichtabs,
609 		tabstop_val(curbp),
610 		is_local_b_val(curbp, val) ? "local" : "global");
611     return TRUE;
612 }
613 
614 /*
615  * Set fill column to n.
616  */
617 int
setfillcol(int f,int n)618 setfillcol(int f, int n)
619 {
620     if (f) {
621 	set_local_b_val(curbp, VAL_FILL, n);
622     }
623     if (!global_b_val(MDTERSE) || !f)
624 	mlwrite("[Fill column is %d, and is %s]",
625 		getfillcol(curbp),
626 		is_local_b_val(curbp, VAL_FILL) ? "local" : "global");
627     return (TRUE);
628 }
629 
630 /*
631  * Returns the effective fill-column
632  */
633 int
getfillcol(BUFFER * bp)634 getfillcol(BUFFER *bp)
635 {
636     int result = b_val(bp, VAL_FILL);
637     if (result == 0) {
638 	result = term.cols - b_val(bp, VAL_WRAPMARGIN);
639     } else if (result < 0) {
640 	result = term.cols + result;
641     }
642     return (result);
643 }
644 
645 /*
646  * Release storage of a REGEXVAL struct
647  */
648 REGEXVAL *
free_regexval(REGEXVAL * rp)649 free_regexval(REGEXVAL * rp)
650 {
651     if (rp != 0) {
652 	beginDisplay();
653 	FreeAndNull(rp->pat);
654 	FreeAndNull(rp->reg);
655 	free((char *) rp);
656 	endofDisplay();
657     }
658     return 0;
659 }
660 
661 /*
662  * Allocate/set a new REGEXVAL struct
663  */
664 REGEXVAL *
new_regexval(const char * pattern,int magic)665 new_regexval(const char *pattern, int magic)
666 {
667     REGEXVAL *rp = 0;
668 
669     if (pattern != 0) {
670 	beginDisplay();
671 	if ((rp = typecalloc(REGEXVAL)) != 0) {
672 	    if ((rp->pat = strmalloc(pattern)) == 0
673 		|| (rp->reg = regcomp(rp->pat, strlen(rp->pat), magic)) == 0)
674 		rp = free_regexval(rp);
675 	}
676 	endofDisplay();
677     }
678 
679     return rp;
680 }
681 
682 /*
683  * Release storage of a VAL struct
684  */
685 void
free_val(const struct VALNAMES * names,struct VAL * values)686 free_val(const struct VALNAMES *names, struct VAL *values)
687 {
688     switch (names->type) {
689     case VALTYPE_STRING:
690 	beginDisplay();
691 	FreeAndNull(values->v.p);
692 	endofDisplay();
693 	break;
694     case VALTYPE_REGEX:
695 	values->v.r = free_regexval(values->v.r);
696 	break;
697     default:			/* nothing to free */
698 	break;
699     }
700 }
701 
702 /*
703  * Copy a VAL-struct, preserving the sense of local/global.
704  */
705 static int
copy_val(struct VAL * dst,struct VAL * src)706 copy_val(struct VAL *dst, struct VAL *src)
707 {
708     int local = isLocalVal(src);
709 
710     *dst = *src;
711     if (local)
712 	makeLocalVal(dst);
713     return local;
714 }
715 
716 void
copy_mvals(int maximum,struct VAL * dst,struct VAL * src)717 copy_mvals(int maximum,
718 	   struct VAL *dst,
719 	   struct VAL *src)
720 {
721     int n;
722     for (n = 0; n < maximum; n++)
723 	(void) copy_val(&dst[n], &src[n]);
724 }
725 
726 /*
727  * Copy a VAL-struct ensuring that strings are allocated anew.
728  */
729 static void
clone_val(struct VAL * dst,struct VAL * src,const struct VALNAMES * names)730 clone_val(struct VAL *dst, struct VAL *src, const struct VALNAMES *names)
731 {
732     REGEXVAL *r;
733 
734     switch (names->type) {
735     case VALTYPE_INT:
736     case VALTYPE_BOOL:
737     case VALTYPE_ENUM:
738 	dst->v.i = src->v.i;
739 	break;
740 
741     case VALTYPE_STRING:
742 	FreeAndNull(dst->v.p);
743 	dst->v.p = strmalloc(src->v.p);
744 	break;
745 
746     case VALTYPE_REGEX:
747 	if (dst->v.r) {
748 	    free_regexval(dst->v.r);
749 	    dst->v.r = 0;
750 	}
751 	if (src->v.r &&
752 	    src->v.r->pat) {
753 	    if ((r = new_regexval(src->v.r->pat, TRUE)) == 0) {
754 		dst->v.r = new_regexval("", TRUE);
755 	    } else {
756 		dst->v.r = r;
757 	    }
758 	}
759 	break;
760     }
761     if (isLocalVal(src)) {
762 	makeLocalVal(dst);
763     }
764 }
765 
766 static void
clone_mvals(int maximum,struct VAL * dst,struct VAL * src,const struct VALNAMES * names)767 clone_mvals(int maximum,
768 	    struct VAL *dst,
769 	    struct VAL *src,
770 	    const struct VALNAMES *names)
771 {
772     int n;
773     for (n = 0; n < maximum; n++)
774 	clone_val(&dst[n], &src[n], &names[n]);
775 }
776 
777 /*
778  * This is a special routine designed to save the values of local modes and to
779  * restore them.  The 'recompute_buffer()' procedure assumes that global modes
780  * do not change during the recomputation process (so there is no point in
781  * trying to convert any of those values to local ones).
782  */
783 #if OPT_UPBUFF
784 void
save_vals(int maximum,struct VAL * gbl,struct VAL * dst,struct VAL * src)785 save_vals(int maximum,
786 	  struct VAL *gbl,
787 	  struct VAL *dst,
788 	  struct VAL *src)
789 {
790     int n;
791     for (n = 0; n < maximum; n++)
792 	if (copy_val(&dst[n], &src[n]))
793 	    make_global_val(src, gbl, n);
794 }
795 #endif
796 
797 /*
798  * free storage used by local mode-values, called only when we are freeing
799  * all other storage associated with a buffer or window.
800  */
801 void
free_local_vals(const struct VALNAMES * names,struct VAL * gbl,struct VAL * val)802 free_local_vals(const struct VALNAMES *names,
803 		struct VAL *gbl,
804 		struct VAL *val)
805 {
806     int j;
807 
808     for (j = 0; names[j].name != 0; j++) {
809 	if (is_local_val(val, j)) {
810 	    make_global_val(val, gbl, j);
811 	    free_val(names + j, val + j);
812 	}
813     }
814 }
815 
816 /*
817  * Convert a string to boolean, checking for errors
818  */
819 static int
string_to_bool(const char * base,int * np)820 string_to_bool(const char *base, int *np)
821 {
822     if (is_truem(base))
823 	*np = TRUE;
824     else if (is_falsem(base))
825 	*np = FALSE;
826     else {
827 	mlforce("[Not a boolean: '%s']", base);
828 	return FALSE;
829     }
830     return TRUE;
831 }
832 
833 /*
834  * Convert a string to number, checking for errors
835  */
836 int
string_to_number(const char * from,int * np)837 string_to_number(const char *from, int *np)
838 {
839     long n;
840     char *p;
841 
842     /* accept decimal, octal, or hex */
843     n = strtol(from, &p, 0);
844     if (p == from || *p != EOS) {
845 	mlforce("[Not a number: '%s']", from);
846 	return FALSE;
847     }
848     *np = (int) n;
849     return TRUE;
850 }
851 
852 /*
853  * Validate a 'glob' mode-value.  It is either a boolean, or it must be a
854  * pipe-expression with exactly one "%s" embedded (no other % characters,
855  * unless escaped).  That way, we can use the string to format the pipe
856  * command.
857  */
858 #if defined(GMD_GLOB) || defined(GVAL_GLOB)
859 static int
legal_glob_mode(const char * base)860 legal_glob_mode(const char *base)
861 {
862 #ifdef GVAL_GLOB		/* string */
863     if (isShellOrPipe(base)) {
864 	const char *s = base;
865 	int count = 0;
866 	while (*s != EOS) {
867 	    if (*s == '%') {
868 		if (*++s != '%') {
869 		    if (*s == 's')
870 			count++;
871 		    else
872 			count = 2;
873 		}
874 	    }
875 	    if (*s != EOS)
876 		s++;
877 	}
878 	if (count == 1)
879 	    return TRUE;
880     }
881 #endif
882     if (is_truem(base)
883 	|| is_falsem(base))
884 	return TRUE;
885 
886     mlforce("[Illegal value for glob: '%s']", base);
887     return FALSE;
888 }
889 #endif
890 
891 /*
892  * FSM stands for fixed string mode, so called because the strings which the
893  * user is permitted to enter are non-arbitrary (fixed).
894  *
895  * It is meant to handle the following sorts of things:
896  *
897  *	:set popup-choices off
898  *	:set popup-choices immediate
899  *	:set popup-choices delayed
900  *
901  *	:set error quiet
902  *	:set error beep
903  *	:set error flash
904  */
905 #if OPT_ENUM_MODES
906 
907 #if VILE_NEVER
908 FSM_CHOICES fsm_error[] =
909 {
910     {"beep", 1},
911     {"flash", 2},
912     {"quiet", 0},
913     END_CHOICES
914 };
915 #endif
916 
917 #if OPT_COLOR_CHOICES
918 static const char s_fcolor[] = "fcolor";
919 static const char s_bcolor[] = "bcolor";
920 static const char s_ccolor[] = "ccolor";
921 #endif
922 
923 #if OPT_COLOR_SCHEMES
924 static const char s_default[] = "default";
925 static const char s_color_scheme[] = "color-scheme";
926 static const char s_palette[] = "palette";
927 static const char s_video_attrs[] = "video-attrs";
928 static const
929 FSM_CHOICES fsm_no_choices[] =
930 {
931     {s_default, 0},
932     END_CHOICES			/* ends table for name-completion */
933 };
934 static FSM_BLIST fsm_no_blist =
935 {
936     fsm_no_choices,
937     init_blist(fsm_no_choices)
938 };
939 #endif /* OPT_COLOR_SCHEMES */
940 
941 static const FSM_TABLE fsm_tbl[] =
942 {
943     {"*bool", &fsm_bool_blist},
944 #if OPT_COLOR_SCHEMES
945     {s_color_scheme, &fsm_no_blist},
946 #endif
947 #if OPT_COLOR_CHOICES
948     {s_fcolor, &fsm_color_blist},
949     {s_bcolor, &fsm_color_blist},
950     {s_ccolor, &fsm_color_blist},
951 #endif
952 #if OPT_CURTOKENS_CHOICES
953     {"cursor-tokens", &fsm_curtokens_blist},
954 #endif
955 #if OPT_POPUP_CHOICES
956     {"popup-choices", &fsm_popup_blist},
957 #endif
958 #if OPT_POPUPPOSITIONS_CHOICES
959     {"popup-positions", &fsm_popuppositions_blist},
960 #endif
961 #if VILE_NEVER
962     {"error", &fsm_error},
963 #endif
964 #if OPT_BACKUP_CHOICES
965     {"backup-style", &fsm_backup_blist},
966 #endif
967 #if OPT_COLOR
968     {s_video_attrs, &fsm_videoattrs_blist},
969 #endif
970 #if OPT_FORBUFFERS_CHOICES
971     {"for-buffers", &fsm_forbuffers_blist},
972 #endif
973 #if OPT_HILITE_CHOICES
974     {"mcolor", &fsm_hilite_blist},
975     {"visual-matches", &fsm_hilite_blist},
976     {"mini-hilite", &fsm_hilite_blist},
977 #endif
978 #if OPT_KEEP_POS
979     {"keep-position", &fsm_keep_pos_blist},
980 #endif
981 #if OPT_LOOKUP_CHOICES
982     {"check-access", &fsm_access_blist},
983 #endif
984 #if OPT_MULTIBYTE
985     {"byteorder-mark", &fsm_byteorder_mark_blist},
986     {"cmd-encoding", &fsm_cmd_encoding_blist},
987     {"kbd-encoding", &fsm_kbd_encoding_blist},
988     {"file-encoding", &fsm_file_encoding_blist},
989     {"title-encoding", &fsm_title_encoding_blist},
990 #endif
991 #if OPT_VTFLASHSEQ_CHOICES
992     {"vtflash", &fsm_vtflashseq_blist},
993 #endif
994 #if SYS_VMS
995     {"record-format", &fsm_recordformat_blist},
996     {"record-attrs", &fsm_recordattrs_blist},
997 #endif
998 #if OPT_READERPOLICY_CHOICES
999     {"reader-policy", &fsm_readerpolicy_blist},
1000 #endif
1001 #if OPT_RECORDSEP_CHOICES
1002     {"recordseparator", &fsm_recordsep_blist},
1003 #endif
1004 #if OPT_SHOWFORMAT_CHOICES
1005     {"showformat", &fsm_showformat_blist},
1006 #endif
1007 #if OPT_MMQUALIFIERS_CHOICES
1008     {"qualifiers", &fsm_mmqualifiers_blist},
1009 #endif
1010 };
1011 
1012 static size_t
fsm_size(const FSM_CHOICES * list)1013 fsm_size(const FSM_CHOICES * list)
1014 {
1015     size_t result = 0;
1016     while ((list++)->choice_name != 0)
1017 	result++;
1018     return result;
1019 }
1020 
1021 FSM_BLIST *
name_to_choices(const char * name)1022 name_to_choices(const char *name)
1023 {
1024     int i;
1025     FSM_BLIST *result = 0;
1026 
1027     fsm_idx = -1;
1028     for (i = 1; i < (int) TABLESIZE(fsm_tbl); i++) {
1029 	if (strcmp(fsm_tbl[i].mode_name, name) == 0) {
1030 	    fsm_idx = i;
1031 	    result = fsm_tbl[i].lists;
1032 	    break;
1033 	}
1034     }
1035     return result;
1036 }
1037 
1038 static FSM_BLIST *
valname_to_choices(const struct VALNAMES * names)1039 valname_to_choices(const struct VALNAMES *names)
1040 {
1041     return name_to_choices(names->name);
1042 }
1043 
1044 static int
is_fsm(const struct VALNAMES * names)1045 is_fsm(const struct VALNAMES *names)
1046 {
1047     size_t i;
1048 
1049     if (names->name != 0
1050 	&& (names->type == VALTYPE_ENUM
1051 	    || names->type == VALTYPE_STRING)) {
1052 	for (i = 1; i < TABLESIZE(fsm_tbl); i++) {
1053 	    if (strcmp(fsm_tbl[i].mode_name, names->name) == 0) {
1054 		fsm_idx = (int) i;
1055 		return TRUE;
1056 	    }
1057 	}
1058     } else if (is_bool_type(names->type)) {
1059 	fsm_idx = 0;
1060 	return TRUE;
1061     }
1062     fsm_idx = -1;
1063     return FALSE;
1064 }
1065 
1066 /*
1067  * Silently check if the string forms a number, to work with checks for FSM's
1068  * that allow numbers instead of names.
1069  */
1070 static int
legal_number(const char * val)1071 legal_number(const char *val)
1072 {
1073     int failed;
1074     (void) vl_atol(val, 0, &failed);
1075     return (failed == 0);
1076 }
1077 
1078 /*
1079  * Test if we're processing an enum-valued mode.  If so, lookup the mode value.
1080  * We'll allow a numeric index also (e.g., for colors).  Note that we're
1081  * returning the table-value in that case, so we'll have to ensure that we
1082  * don't corrupt the table.
1083  */
1084 static const char *
legal_fsm(const char * val)1085 legal_fsm(const char *val)
1086 {
1087     if (fsm_idx >= 0) {
1088 	int i;
1089 	int idx = fsm_idx;
1090 	FSM_BLIST *p = fsm_tbl[idx].lists;
1091 	const char *s;
1092 	int failed = FALSE;
1093 
1094 	if (isDigit(*val) && legal_number(val)) {
1095 	    if (string_to_number(val, &i)
1096 		&& (s = choice_to_name(p, i)) != 0) {
1097 		val = s;
1098 	    } else {
1099 		failed = TRUE;
1100 	    }
1101 	} else {
1102 	    if (choice_to_code(p, val, strlen(val)) == ENUM_ILLEGAL) {
1103 		failed = TRUE;
1104 	    }
1105 	}
1106 	if (failed) {
1107 	    mlforce("[Illegal value for %s: '%s']",
1108 		    fsm_tbl[idx].mode_name,
1109 		    val);
1110 	    val = 0;
1111 	}
1112     }
1113     return val;
1114 }
1115 
1116 int
fsm_complete(DONE_ARGS)1117 fsm_complete(DONE_ARGS)
1118 {
1119     int result = FALSE;
1120 
1121     if (buf != 0) {
1122 	if (isDigit(*buf)) {	/* allow numbers for colors */
1123 	    if (c != NAMEC && c != TESTC) {
1124 		/* put it back (cf: kbd_complete) */
1125 		unkeystroke(c);
1126 		result = isSpace(c);
1127 	    }
1128 	}
1129 	if (!result) {
1130 	    (void) mklower(buf);
1131 	    result = kbd_complete(PASS_DONE_ARGS,
1132 				  (const char *) (fsm_tbl[fsm_idx].lists->choices),
1133 				  sizeof(FSM_CHOICES));
1134 	}
1135     }
1136     return result;
1137 }
1138 #endif /* OPT_ENUM_MODES */
1139 
1140 static int
ok_local_mode(void)1141 ok_local_mode(void)
1142 {
1143 #if OPT_MODELINE
1144     /*
1145      * When processing modeline, disallow major changes.
1146      */
1147     if (in_modeline) {
1148 	TRACE(("ignored (within modeline)\n"));
1149 	return FALSE;
1150     }
1151 #endif
1152     return TRUE;
1153 }
1154 
1155 /*
1156  * Lookup the mode named with 'cp[]' and adjust its value.
1157  */
1158 int
adjvalueset(const char * cp,int defining,int setting,int global,VALARGS * args)1159 adjvalueset(const char *cp,	/* name of the mode we are changing */
1160 	    int defining,	/* for majormodes, suppress side_effect */
1161 	    int setting,	/* true if setting, false if unsetting */
1162 	    int global,
1163 	    VALARGS * args)
1164 {				/* symbol-table entry for the mode */
1165     const struct VALNAMES *names = args->names;
1166     struct VAL *values = args->local;
1167     struct VAL *globls = args->global;
1168 
1169     char prompt[NLINE];
1170     char respbuf[NFILEN];
1171     int no;
1172     const char *rp = NULL;
1173     int status;
1174 
1175     if (isEmpty(cp))
1176 	return FALSE;
1177 
1178     no = noPrefix(cp);
1179     if (no && !is_bool_type(names->type))
1180 	return FALSE;		/* this shouldn't happen */
1181 
1182     if ((names->side_effect == chgd_major) && !ok_local_mode()) {
1183 	return FALSE;
1184     }
1185 
1186     /*
1187      * Check if we're allowed to change this mode in the current context.
1188      */
1189     if (!defining
1190 	&& (names->side_effect != 0)
1191 	&& !(*(names->side_effect)) (curbp, args, (values == globls), TRUE)) {
1192 	return FALSE;
1193     }
1194 
1195     /* get a value if we need one */
1196     if ((end_string() == '=')
1197 	|| (!is_bool_type(names->type) && setting)) {
1198 	int regex = (names->type == VALTYPE_REGEX);
1199 	KBD_OPTIONS opts = regex ? 0 : KBD_NORMAL;
1200 	int eolchar = is_str_type(names->type) ? '\n' : ' ';
1201 	int (*complete) (DONE_ARGS) = no_completion;
1202 
1203 	respbuf[0] = EOS;
1204 	(void) lsprintf(prompt, "New %s %s: ",
1205 			names->name ? names->name : cp,
1206 			regex ? "pattern" : "value");
1207 
1208 #if OPT_ENUM_MODES
1209 	if (is_fsm(names)) {
1210 	    complete = fsm_complete;
1211 	    opts |= KBD_LOWERC;
1212 	}
1213 #endif
1214 
1215 	status = kbd_string(prompt, respbuf, sizeof(respbuf), eolchar,
1216 			    opts, complete);
1217 	if (status != TRUE)
1218 	    return status;
1219 
1220 	/*
1221 	 * Allow an empty response to a string-value if we're running in a
1222 	 * macro rather than interactively.
1223 	 */
1224 	if (!strlen(rp = respbuf)
1225 	    && !clexec) {
1226 	    switch (names->type) {
1227 	    case VALTYPE_REGEX:	/* FALLTHRU */
1228 	    case VALTYPE_STRING:
1229 		break;
1230 	    default:
1231 		return FALSE;
1232 	    }
1233 	}
1234     } else if (!setting) {
1235 	rp = is_str_type(names->type) ? "" : "0";
1236     }
1237 #if OPT_HISTORY
1238     else
1239 	hst_glue(' ');
1240 #endif
1241     status = set_mode_value(curbp, cp, defining, setting, global, args, rp);
1242     TRACE(("...adjvalueset(%s)=%d\n", cp, status));
1243 
1244     return status;
1245 }
1246 
1247 int
set_mode_value(BUFFER * bp,const char * cp,int defining,int setting,int global,VALARGS * args,const char * rp)1248 set_mode_value(BUFFER *bp,
1249 	       const char *cp,
1250 	       int defining,
1251 	       int setting,
1252 	       int global,
1253 	       VALARGS * args,
1254 	       const char *rp)
1255 {
1256     const struct VALNAMES *names = args->names;
1257     struct VAL *values = args->local;
1258     struct VAL *globls = args->global;
1259     REGEXVAL *r;
1260 
1261     struct VAL oldvalue;
1262     int no = noPrefix(cp);
1263     int nval, status;
1264     int unsetting = !setting && !global;
1265     int changed = FALSE;
1266     int free_old = 0;
1267 
1268     /*
1269      * Check if we're allowed to change this mode in the current context.
1270      */
1271     if (!defining
1272 	&& (names->side_effect != 0)
1273 	&& !(*(names->side_effect)) (bp, args, (values == globls), TRUE)) {
1274 	return FALSE;
1275     }
1276 
1277     if (rp == NULL) {
1278 	rp = no ? cp + 2 : cp;
1279     } else {
1280 	if (no && !is_bool_type(names->type))
1281 	    return FALSE;	/* this shouldn't happen */
1282 
1283 #if defined(GMD_GLOB) || defined(GVAL_GLOB)
1284 	if (names->name != 0
1285 	    && !strcmp(names->name, "glob")
1286 	    && !legal_glob_mode(rp))
1287 	    return FALSE;
1288 #endif
1289 #if OPT_ENUM_MODES
1290 	(void) is_fsm(names);	/* evaluated for its side effects */
1291 	if ((rp = legal_fsm(rp)) == 0)
1292 	    return FALSE;
1293 #endif
1294 	/* Test after fsm, to allow translation */
1295 	if (is_bool_type(names->type)) {
1296 	    if (!string_to_bool(rp, &setting))
1297 		return FALSE;
1298 	}
1299     }
1300 
1301     /* save, to simplify no-change testing */
1302     (void) copy_val(&oldvalue, values);
1303 
1304     if (unsetting) {
1305 	make_global_val(values, globls, 0);
1306 #if OPT_MAJORMODE
1307 	switch (names->type) {
1308 	case VALTYPE_MAJOR:
1309 	    if (values == globls)
1310 		changed = enable_mmode(names->shortname, FALSE);
1311 	    else
1312 		changed = detach_mmode(bp, names->shortname);
1313 	    break;
1314 	}
1315 #endif
1316     } else {
1317 	/* make sure we point to result! */
1318 	if (global) {
1319 	    make_global_val(values, globls, 0);
1320 	} else {
1321 	    makeLocalVal(values);
1322 	}
1323 
1324 	/* we matched a name -- set the value */
1325 	switch (names->type) {
1326 #if OPT_MAJORMODE
1327 	case VALTYPE_MAJOR:
1328 	    values->vp->i = no ? !setting : setting;
1329 	    if (values == globls) {
1330 		changed = enable_mmode(names->shortname, values->vp->i);
1331 	    } else {
1332 		changed = no
1333 		    ? detach_mmode(bp, names->shortname)
1334 		    : attach_mmode(bp, names->shortname);
1335 		if (changed)
1336 		    did_attach_mmode = TRUE;
1337 	    }
1338 	    break;
1339 #endif
1340 	case VALTYPE_BOOL:
1341 	    values->vp->i = no ? !setting : setting;
1342 	    break;
1343 
1344 	case VALTYPE_ENUM:
1345 #if OPT_ENUM_MODES
1346 	    {
1347 		FSM_BLIST *fp = valname_to_choices(names);
1348 
1349 		if (legal_number(rp)) {
1350 		    if (!string_to_number(rp, &nval))
1351 			return FALSE;
1352 		    if (choice_to_name(fp, nval) == 0)
1353 			nval = ENUM_ILLEGAL;
1354 		} else {
1355 		    nval = choice_to_code(fp, rp, strlen(rp));
1356 		}
1357 		if (nval == ENUM_ILLEGAL) {
1358 		    mlforce("[Not a legal enum-index: %s]",
1359 			    rp);
1360 		    return FALSE;
1361 		}
1362 	    }
1363 	    values->vp->i = nval;
1364 	    break;
1365 #endif /* OPT_ENUM_MODES */
1366 
1367 	case VALTYPE_INT:
1368 	    if (!string_to_number(rp, &nval))
1369 		return FALSE;
1370 	    values->vp->i = nval;
1371 	    break;
1372 
1373 	case VALTYPE_STRING:
1374 	    free_old = isLocalVal(&oldvalue);
1375 	    values->vp->p = strmalloc(rp);
1376 	    break;
1377 
1378 	case VALTYPE_REGEX:
1379 	    free_old = isLocalVal(&oldvalue);
1380 	    if ((r = new_regexval(rp, TRUE)) == 0) {
1381 		values->vp->r = new_regexval("", TRUE);
1382 		return FALSE;
1383 	    }
1384 	    values->vp->r = r;
1385 	    break;
1386 
1387 	default:
1388 	    mlforce("BUG: bad type %s %d", names->name, names->type);
1389 	    return FALSE;
1390 	}
1391     }
1392 
1393     /*
1394      * Set window flags (to force the redisplay as needed), and apply
1395      * side-effects.
1396      */
1397     status = TRUE;
1398     if (!same_val(names, values, &oldvalue)) {
1399 	changed = TRUE;
1400     }
1401 
1402     if (!defining
1403 	&& changed
1404 	&& (names->side_effect != 0)
1405 	&& !(*(names->side_effect)) (bp, args, (values == globls), FALSE)) {
1406 	if (!same_val(names, values, &oldvalue)) {
1407 	    free_val(names, values);
1408 	}
1409 	(void) copy_val(values, &oldvalue);
1410 	mlforce("[Cannot set this value]");
1411 	status = FALSE;
1412     } else if (values == globls) {
1413 	free_val(names, &oldvalue);
1414     } else if (free_old && isLocalVal(&oldvalue)) {
1415 	free_val(names, &oldvalue);
1416     }
1417 
1418     return status;
1419 }
1420 
1421 static int last_listmodes_f;
1422 static int last_listmodes_n;
1423 
1424 /* ARGSUSED */
1425 int
listmodes(int f,int n GCC_UNUSED)1426 listmodes(int f, int n GCC_UNUSED)
1427 {
1428     WINDOW *wp = curwp;
1429     int s;
1430 
1431     TRACE((T_CALLED "listmodes(f=%d)\n", f));
1432     TPRINTF(("listmodes(f=%d)\n", f));
1433 
1434     last_listmodes_f = f;
1435     last_listmodes_n = n;
1436 
1437     s = liststuff(SETTINGS_BufName, FALSE, makemodelist, f, (void *) wp);
1438     /* back to the buffer whose modes we just listed */
1439     if (swbuffer(wp->w_bufp))
1440 	curwp = wp;
1441     returnCode(s);
1442 }
1443 
1444 /* ARGSUSED */
1445 int
list_lmodes(int f GCC_UNUSED,int n GCC_UNUSED)1446 list_lmodes(int f GCC_UNUSED, int n GCC_UNUSED)
1447 {
1448     /* the repeat-count is obscure - provide explicit list of local modes */
1449     return listmodes(TRUE, 1);
1450 }
1451 
1452 /*
1453  * The 'mode_complete()' and 'mode_eol()' functions are invoked from
1454  * 'kbd_reply()' to setup the mode-name completion and query displays.
1455  */
1456 static int
mode_complete(DONE_ARGS)1457 mode_complete(DONE_ARGS)
1458 {
1459     init_my_mode_list();
1460 
1461     return kbd_complete(PASS_DONE_ARGS,
1462 			(const char *) &my_mode_list[0], sizeof(my_mode_list[0]));
1463 }
1464 
1465 int
1466 /*ARGSUSED*/
mode_eol(EOL_ARGS)1467 mode_eol(EOL_ARGS)
1468 {
1469     (void) buffer;
1470     (void) cpos;
1471 
1472     return (c == ' ' || c == eolchar);
1473 }
1474 
1475 int
lookup_valnames(const char * rp,const struct VALNAMES * table)1476 lookup_valnames(const char *rp, const struct VALNAMES *table)
1477 {
1478     int j;
1479 
1480     if (rp != 0) {
1481 	for (j = 0; table[j].name != 0; j++) {
1482 	    if (!strcmp(rp, table[j].name)
1483 		|| !strcmp(rp, table[j].shortname)) {
1484 		return j;
1485 	    }
1486 	}
1487     }
1488     return -1;
1489 }
1490 
1491 #define strip_no(mode) (noPrefix(mode) ? (mode + 2) : mode)
1492 
1493 #if OPT_MAJORMODE
1494 /*
1495  * Find a submode value for the current majormode.
1496  */
1497 int
find_submode(BUFFER * bp,const char * mode,int global,VALARGS * args)1498 find_submode(BUFFER *bp, const char *mode, int global, VALARGS * args)
1499 {
1500     const char *rp = strip_no(mode);
1501     int j;
1502 
1503     /* major submodes (buffers) */
1504     if (my_majormodes != 0) {
1505 	int k = 0;
1506 	size_t n = strlen(rp);
1507 
1508 	for (j = 0; my_majormodes[j].shortname; j++) {
1509 	    MAJORMODE_LIST *p = my_majormodes + j;
1510 	    struct VAL *my_vals = get_sm_vals(p->data);
1511 	    size_t len = strlen(p->shortname);
1512 
1513 	    if (n >= len
1514 		&& !strncmp(rp, p->shortname, len)
1515 		&& (k = lookup_valnames(rp + len + (rp[len] == '-'),
1516 					b_valnames)) >= 0
1517 		&& is_local_val(my_vals, k)) {
1518 		TRACE(("...found submode %s\n", b_valnames[k].name));
1519 		if (global == FALSE) {
1520 		    if (valid_buffer(bp)
1521 			&& (bp->majr == 0
1522 			    || strcmp(bp->majr->shortname, p->shortname))) {
1523 			TRACE(("...not applicable\n"));
1524 			return FALSE;
1525 		    }
1526 		    args->names = b_valnames + k;
1527 		    args->global = my_vals + k;
1528 		    args->local = (valid_buffer(bp)
1529 				   ? bp->b_values.bv + k
1530 				   : (struct VAL *) 0);
1531 		} else {
1532 		    args->names = b_valnames + k;
1533 		    args->global = my_vals + k;
1534 		    args->local = args->global;
1535 		}
1536 		return TRUE;
1537 	    }
1538 	}
1539     }
1540     return FALSE;
1541 }
1542 #endif
1543 
1544 /*
1545  * Check if the given mode exists for the given class.  If so, fill in its
1546  * data and return true.  Otherwise return false.  The 'args' data is filled
1547  * in unless there is no such mode class.
1548  */
1549 int
find_mode_class(BUFFER * bp,const char * mode,int global,VALARGS * args,MODECLASS mode_class)1550 find_mode_class(BUFFER *bp,
1551 		const char *mode,
1552 		int global,
1553 		VALARGS * args,
1554 		MODECLASS mode_class)
1555 {
1556     const char *rp = strip_no(mode);
1557     int j;
1558 
1559     memset(args, 0, sizeof(*args));
1560     switch (mode_class) {
1561     default:
1562     case UNI_MODE:		/* universal modes */
1563 	args->names = g_valnames;
1564 	args->global = global_g_values.gv;
1565 	args->local = ((global !=FALSE)
1566 		       ? args->global
1567 		       : (struct VAL *)0);
1568 	break;
1569     case BUF_MODE:		/* buffer modes */
1570 	args->names = b_valnames;
1571 	args->global = global_b_values.bv;
1572 	args->local = ((global == TRUE)
1573 		       ? args->global
1574 		       : (valid_buffer(bp)
1575 			  ? bp->b_values.bv
1576 			  : (struct VAL *)0));
1577 	break;
1578     case WIN_MODE:		/* window modes */
1579 	args->names = w_valnames;
1580 	args->global = global_w_values.wv;
1581 	args->local = ((global == TRUE)
1582 		       ? args->global
1583 		       : ((curwp != 0)
1584 			  ? curwp->w_values.wv
1585 			  : (struct VAL *)0));
1586 	break;
1587 #if OPT_MAJORMODE
1588     case MAJ_MODE:		/* major modes */
1589 	args->names = major_valnames;
1590 	args->global = major_g_vals;
1591 	args->local = ((global == TRUE)
1592 		       ? args->global
1593 		       : (valid_buffer(bp)
1594 			  ? major_l_vals
1595 			  : (struct VAL *)0));
1596 	break;
1597     case SUB_MODE:		/* major submodes (qualifiers) */
1598 	if (my_majormodes != 0) {
1599 	    size_t n = strlen(rp);
1600 
1601 	    for (j = 0; my_majormodes[j].shortname; j++) {
1602 		MAJORMODE_LIST *p = my_majormodes + j;
1603 		size_t len = strlen(p->shortname);
1604 
1605 		if (n >= len
1606 		    && !strncmp(rp, p->shortname, len)
1607 		    && (lookup_valnames(rp, p->qual)) >= 0) {
1608 		    args->names = p->qual;
1609 		    args->global = p->data->mm.mv;
1610 		    args->local = ((global !=FALSE)
1611 				   ? args->global
1612 				   : (struct VAL *)0);
1613 		    break;
1614 		}
1615 	    }
1616 	}
1617 	break;
1618 #endif
1619     }
1620     if (args->names != 0
1621 	&& args->local != 0) {
1622 	if ((j = lookup_valnames(rp, args->names)) >= 0) {
1623 	    args->names += j;
1624 	    args->local += j;
1625 	    args->global +=j;
1626 	    TRACE(("...found class %d %s\n", mode_class, rp));
1627 #if OPT_MAJORMODE
1628 	    if (mode_class == MAJ_MODE) {
1629 		const char *it = (valid_buffer(bp) && (bp->majr != 0)
1630 				  ? bp->majr->shortname
1631 				  : "?");
1632 		make_global_val(args->local, args->global, 0);
1633 		if (global) {
1634 		    MAJORMODE_LIST *ptr = lookup_mm_list(it);
1635 		    args->local[0].v.i = (ptr != 0 && ptr->flag);
1636 		} else {
1637 		    char temp[NSTRING];
1638 		    majorname(temp, it, TRUE);
1639 		    make_local_val(args->local, 0);
1640 		    args->local[0].v.i = !strcmp(temp, rp);
1641 		}
1642 	    }
1643 #endif
1644 	    return TRUE;
1645 	}
1646     }
1647     return FALSE;
1648 }
1649 
1650 /*
1651  * Search the list of modes across all classes to find the first match, and
1652  * fill in the corresponding data, returning true.  If no match is found,
1653  * return false.
1654  */
1655 int
find_mode(BUFFER * bp,const char * mode,int global,VALARGS * args)1656 find_mode(BUFFER *bp, const char *mode, int global, VALARGS * args)
1657 {
1658     int mode_class;
1659 
1660     TRACE((T_CALLED "find_mode(%s) %s\n", mode, global ? "global" : "local"));
1661 
1662     for (mode_class = 0; mode_class < END_MODE; mode_class++) {
1663 	if (find_mode_class(bp, mode, global, args, (MODECLASS) mode_class))
1664 	      returnCode(TRUE);
1665     }
1666 #if OPT_MAJORMODE
1667     if (find_submode(bp, mode, global, args)) {
1668 	returnCode(TRUE);
1669     }
1670 #endif
1671     returnCode(FALSE);
1672 }
1673 
1674 /*
1675  * Process a single mode-setting
1676  */
1677 static int
do_a_mode(int kind,int global)1678 do_a_mode(int kind, int global)
1679 {
1680     int rc = FALSE;
1681     VALARGS args;
1682     int s;
1683     static TBUFF *cbuf;		/* buffer to receive mode name into */
1684 
1685     /* prompt the user and get an answer */
1686     /* *INDENT-OFF* */
1687     tb_scopy(&cbuf, "");
1688     if ((s = kbd_reply((global
1689 			? "Global value: "
1690 			: "Local value: "),
1691 		       &cbuf,
1692 		       mode_eol, '=', KBD_NORMAL, mode_complete)) != TRUE) {
1693 	rc = ((s == FALSE) ? SORTOFTRUE : s);
1694 #if OPT_MODELINE
1695 	if (clexec && (in_modeline >= 2) && (s == ABORT)) {
1696 	    TRACE(("Recovering from unsupported vi-mode:%s\n",
1697 		   tb_values(cbuf)));
1698 	    if (end_string() == '=') {
1699 		char respbuf[NSTRING];
1700 		kbd_string("", respbuf, sizeof(respbuf), ' ',
1701 			    KBD_NORMAL, no_completion);
1702 	    }
1703 	    TRACE(("...done recovering...\n"));
1704 	    rc = TRUE;
1705 	}
1706 #endif
1707     } else if (tb_length(cbuf) == 0) {
1708 	rc = FALSE;
1709     } else if (!strcmp(tb_values(cbuf), "all")) {
1710 	if (ok_local_mode()) {
1711 	    hst_glue(' ');
1712 	    rc = listmodes(last_listmodes_f, last_listmodes_n);
1713 	}
1714     } else if (find_mode(curbp, tb_values(cbuf), global, &args) != TRUE) {
1715 #if OPT_EVAL
1716 	if (global) {
1717 	    rc = set_state_variable(tb_values(cbuf), NULL);
1718 	} else if (find_mode(curbp, tb_values(cbuf), TRUE, &args) != TRUE) {
1719 	    rc = set_state_variable(tb_values(cbuf), NULL);
1720 	} else {
1721 	    mlforce("[Not a local mode: \"%s\"]", tb_values(cbuf));
1722 	}
1723 #else
1724 	mlforce("[Not a legal set option: \"%s\"]", tb_values(cbuf));
1725 #endif
1726     } else if ((s = adjvalueset(tb_values(cbuf), FALSE, kind, global, &args)) != 0) {
1727 	if (s == TRUE) {
1728 	    mlerase();	/* erase the junk */
1729 	}
1730 	rc = s;
1731     } else {
1732 	rc = FALSE;
1733     }
1734     /* *INDENT-ON* */
1735 
1736     return rc;
1737 }
1738 
1739 /*
1740  * Process the list of mode-settings.
1741  * If 'kind' is true, set otherwise delete.
1742  */
1743 static int
adjustmode(int kind,int global)1744 adjustmode(int kind, int global)
1745 {
1746     int s;
1747     int anything = 0;
1748 
1749     if (kind && global &&isreturn(end_string()))
1750 	return listmodes(last_listmodes_f, last_listmodes_n);
1751 
1752     while (((s = do_a_mode(kind, global)) == TRUE) && (end_string() == ' '))
1753 	  anything++;
1754     if ((s == SORTOFTRUE) && anything)	/* fix for trailing whitespace */
1755 	return TRUE;
1756 
1757     /* if the settings are up, redisplay them */
1758     relist_settings();
1759     relist_majormodes();
1760 
1761     return s;
1762 }
1763 
1764 /*
1765  * Buffer-animation for [Settings]
1766  */
1767 #if OPT_UPBUFF
1768 static int
show_Settings(BUFFER * bp)1769 show_Settings(BUFFER *bp)
1770 {
1771     b_clr_obsolete(bp);
1772     return listmodes(last_listmodes_f, last_listmodes_n);
1773 }
1774 
1775 static void
relist_settings(void)1776 relist_settings(void)
1777 {
1778     update_scratch(SETTINGS_BufName, show_Settings);
1779 }
1780 #endif /* OPT_UPBUFF */
1781 
1782 /* prompt and set an editor mode */
1783 /* ARGSUSED */
1784 int
setlocmode(int f GCC_UNUSED,int n GCC_UNUSED)1785 setlocmode(int f GCC_UNUSED, int n GCC_UNUSED)
1786 {
1787     return adjustmode(TRUE, FALSE);
1788 }
1789 
1790 /* prompt and delete an editor mode */
1791 /* ARGSUSED */
1792 int
dellocmode(int f GCC_UNUSED,int n GCC_UNUSED)1793 dellocmode(int f GCC_UNUSED, int n GCC_UNUSED)
1794 {
1795     return adjustmode(FALSE, FALSE);
1796 }
1797 
1798 /* prompt and set a global editor mode */
1799 /* ARGSUSED */
1800 int
setglobmode(int f GCC_UNUSED,int n GCC_UNUSED)1801 setglobmode(int f GCC_UNUSED, int n GCC_UNUSED)
1802 {
1803     return adjustmode(TRUE, TRUE);
1804 }
1805 
1806 /* prompt and delete a global editor mode */
1807 /* ARGSUSED */
1808 int
delglobmode(int f GCC_UNUSED,int n GCC_UNUSED)1809 delglobmode(int f GCC_UNUSED, int n GCC_UNUSED)
1810 {
1811     return adjustmode(FALSE, TRUE);
1812 }
1813 
1814 /*
1815  * The following functions are invoked to carry out side effects of changing
1816  * modes.
1817  */
1818 /*ARGSUSED*/
1819 int
chgd_autobuf(BUFFER * bp GCC_UNUSED,VALARGS * args GCC_UNUSED,int glob_vals,int testing GCC_UNUSED)1820 chgd_autobuf(BUFFER *bp GCC_UNUSED,
1821 	     VALARGS * args GCC_UNUSED,
1822 	     int glob_vals,
1823 	     int testing GCC_UNUSED)
1824 {
1825     if (glob_vals)
1826 	sortlistbuffers();
1827     return TRUE;
1828 }
1829 
1830 /*ARGSUSED*/
1831 int
chgd_buffer(BUFFER * bp GCC_UNUSED,VALARGS * args GCC_UNUSED,int glob_vals,int testing GCC_UNUSED)1832 chgd_buffer(BUFFER *bp GCC_UNUSED,
1833 	    VALARGS * args GCC_UNUSED,
1834 	    int glob_vals,
1835 	    int testing GCC_UNUSED)
1836 {
1837     if (!glob_vals) {		/* i.e., ":setl" */
1838 	if (bp == 0)
1839 	    return FALSE;
1840 	b_clr_counted(bp);
1841 	(void) bsizes(bp);
1842     }
1843     return TRUE;
1844 }
1845 
1846 int
chgd_charset(BUFFER * bp,VALARGS * args,int glob_vals,int testing)1847 chgd_charset(BUFFER *bp, VALARGS * args, int glob_vals, int testing)
1848 {
1849     if (!testing) {
1850 	rebuild_charclasses(global_g_val(GVAL_PRINT_LOW),
1851 			    global_g_val(GVAL_PRINT_HIGH));
1852     }
1853     return chgd_window(bp, args, glob_vals, testing);
1854 }
1855 
1856 #if OPT_COLOR
1857 int
chgd_color(BUFFER * bp GCC_UNUSED,VALARGS * args,int glob_vals,int testing)1858 chgd_color(BUFFER *bp GCC_UNUSED, VALARGS * args, int glob_vals, int testing)
1859 {
1860     if (!testing) {
1861 	if (&args->local->vp->i == &gfcolor)
1862 	    term.setfore(gfcolor);
1863 	else if (&args->local->vp->i == &gbcolor)
1864 	    term.setback(gbcolor);
1865 	else if (&args->local->vp->i == &gccolor)
1866 	    term.setccol(gccolor);
1867 	set_winflags(glob_vals, WFHARD | WFCOLR);
1868 	need_update = TRUE;
1869     }
1870     return TRUE;
1871 }
1872 
1873 #if OPT_ENUM_MODES || OPT_COLOR_SCHEMES
1874 static void
set_fsm_choice(const char * name,const FSM_CHOICES * choices)1875 set_fsm_choice(const char *name, const FSM_CHOICES * choices)
1876 {
1877     size_t n;
1878 
1879     TRACE((T_CALLED "set_fsm_choices(%s)\n", name));
1880 #if OPT_TRACE
1881     for (n = 0; choices[n].choice_name != 0; n++)
1882 	TRACE(("   [%d] %s = %d (%#x)\n", (int) n,
1883 	       choices[n].choice_name,
1884 	       choices[n].choice_code,
1885 	       choices[n].choice_code));
1886 #endif
1887     for (n = 0; n < TABLESIZE(fsm_tbl); n++) {
1888 	if (!strcmp(name, fsm_tbl[n].mode_name)) {
1889 	    blist_reset(&(fsm_tbl[n].lists->blist),
1890 			fsm_tbl[n].lists->choices = choices);
1891 	    break;
1892 	}
1893     }
1894     returnVoid();
1895 }
1896 #endif /* OPT_ENUM_MODES || OPT_COLOR_SCHEMES */
1897 
1898 static int
is_white(int n)1899 is_white(int n)
1900 {
1901     if (ncolors <= 8)
1902 	return ((n & 7) == 7);
1903     return (n == NCOLORS - 1);
1904 }
1905 
1906 static int
reset_color(int n)1907 reset_color(int n)
1908 {
1909     int oldvalue = global_g_val(n);
1910     if (oldvalue > ncolors && !is_white(oldvalue)) {
1911 	set_global_g_val(n, oldvalue % ncolors);
1912 	TRACE(("reset_color(%scolor) from %d to %d\n",
1913 	       (n == GVAL_FCOLOR) ? "f" : "b",
1914 	       oldvalue,
1915 	       global_g_val(n)));
1916 	return TRUE;
1917     }
1918     return FALSE;
1919 }
1920 
1921 #if OPT_ENUM_MODES
1922 static FSM_CHOICES *my_colors;
1923 static FSM_CHOICES *my_hilite;
1924 
1925 static int
choosable_color(const char * name,int n)1926 choosable_color(const char *name, int n)
1927 {
1928     if (ncolors > 2 && n < NCOLORS) {
1929 	if (ncolors <= 8	/* filter out misleading names */
1930 	    && (!strncmp(name, "bright", (size_t) 6)
1931 		|| !strncmp(name, "light", (size_t) 5)
1932 		|| !strcmp(name, "gray")
1933 		|| !strcmp(name, "brown")))
1934 	    return FALSE;
1935 	return TRUE;
1936     }
1937     return (n == C_BLACK) || (n == NCOLORS - 1);
1938 }
1939 #endif
1940 
1941 /*
1942  * Set the number of colors to a subset of that which is configured.  The main
1943  * use for this is to switch between 16-colors and 8-colors, though it should
1944  * work for setting any value up to the NCOLORS value.
1945  */
1946 int
set_colors(int n)1947 set_colors(int n)
1948 {
1949     static int initialized;
1950 #if OPT_ENUM_MODES
1951     const FSM_CHOICES *the_colors, *the_hilite;
1952     size_t s, d;
1953     int code;
1954 #endif
1955 
1956     TRACE((T_CALLED "set_colors(%d)\n", n));
1957 
1958     if (n > NCOLORS || n < 2)
1959 	returnCode(FALSE);
1960     if (!initialized)
1961 	initialized = n;
1962     if (n > initialized)
1963 	returnCode(FALSE);
1964     ncolors = n;
1965 
1966     if (reset_color(GVAL_FCOLOR)
1967 	|| reset_color(GVAL_BCOLOR)) {
1968 	set_winflags(TRUE, WFHARD | WFCOLR);
1969 	need_update = TRUE;
1970 	relist_settings();
1971     }
1972 #if OPT_ENUM_MODES
1973     if (ncolors == NCOLORS) {
1974 	the_colors = fsm_color_choices;
1975 	the_hilite = fsm_hilite_choices;
1976     } else {
1977 
1978 	beginDisplay();
1979 	my_colors = typecallocn(FSM_CHOICES, 1 + fsm_size(fsm_color_choices));
1980 	my_hilite = typecallocn(FSM_CHOICES, 1 + fsm_size(fsm_hilite_choices));
1981 	endofDisplay();
1982 
1983 	if (my_colors == 0
1984 	    || my_hilite == 0) {
1985 	    FreeIfNeeded(my_colors);
1986 	    return FALSE;
1987 	}
1988 
1989 	the_colors = my_colors;
1990 	the_hilite = my_hilite;
1991 	for (s = d = 0; fsm_color_choices[s].choice_name != 0; s++) {
1992 	    my_colors[d] = fsm_color_choices[s];
1993 	    if (choosable_color(my_colors[d].choice_name, my_colors[d].choice_code)) {
1994 		if (ncolors <= 8)
1995 		    my_colors[d].choice_code %= 8;
1996 		d++;
1997 	    }
1998 	}
1999 	my_colors[d].choice_name = 0;
2000 
2001 	for (s = d = 0; fsm_hilite_choices[s].choice_name != 0; s++) {
2002 	    my_hilite[d] = fsm_hilite_choices[s];
2003 	    code = my_hilite[d].choice_code;
2004 	    if (code >= 0 && (code & VASPCOL) != 0) {
2005 		if (ncolors > 2
2006 		    && choosable_color(my_hilite[d].choice_name, VCOLORNUM(code))) {
2007 		    my_hilite[d].choice_code = (VASPCOL | code);
2008 		    d++;
2009 		}
2010 	    } else if (ncolors > 2 || (code & VACOLOR) == 0) {
2011 		d++;
2012 	    }
2013 	}
2014 	my_hilite[d].choice_name = 0;
2015     }
2016     set_fsm_choice(s_fcolor, the_colors);
2017     set_fsm_choice(s_bcolor, the_colors);
2018     set_fsm_choice(s_ccolor, the_colors);
2019     set_fsm_choice("visual-matches", the_hilite);
2020     set_fsm_choice("mini-hilite", the_hilite);
2021     relist_descolor();
2022 #endif /* OPT_ENUM_MODES */
2023     returnCode(TRUE);
2024 }
2025 #endif /* OPT_COLOR */
2026 
2027 #if OPT_EXTRA_COLOR
2028 int *
lookup_extra_color(XCOLOR_CODES code)2029 lookup_extra_color(XCOLOR_CODES code)
2030 {
2031     int *result = 0;
2032 
2033     switch (code) {
2034     case XCOLOR_NONE:
2035     case XCOLOR_MAX:
2036 	break;
2037     case XCOLOR_ENUM:
2038     case XCOLOR_HYPERTEXT:
2039     case XCOLOR_ISEARCH:
2040     case XCOLOR_LINEBREAK:
2041     case XCOLOR_LINENUMBER:
2042     case XCOLOR_NUMBER:
2043     case XCOLOR_REGEX:
2044     case XCOLOR_STRING:
2045     case XCOLOR_WARNING:
2046 #if !OPT_HILITEMATCH
2047     case XCOLOR_MODELINE:
2048 #endif
2049 	result = extra_colors + code;
2050 	break;
2051 #if OPT_HILITEMATCH
2052     case XCOLOR_MODELINE:
2053 	break;
2054 #endif
2055     }
2056     return result;
2057 }
2058 
2059 /*
2060  * This will show the foreground colors, which we can display with attributes.
2061  */
2062 /* ARGSUSED */
2063 static void
make_xcolor_list(int dum1 GCC_UNUSED,void * ptr GCC_UNUSED)2064 make_xcolor_list(int dum1 GCC_UNUSED, void *ptr GCC_UNUSED)
2065 {
2066     unsigned n;
2067     XCOLOR_CODES code;
2068     int *valuep;
2069     int value;
2070 
2071     bprintf("--- Extra Colors ");
2072     bpadc('-', term.cols - DOT.o);
2073     bputc('\n');
2074 
2075     for (n = 0; fsm_xcolors_choices[n].choice_name != 0; ++n) {
2076 	bprintf("\n   %s", fsm_xcolors_choices[n].choice_name);
2077 	bpadc(' ', 20 - DOT.o);
2078 	code = (XCOLOR_CODES) fsm_xcolors_choices[n].choice_code;
2079 	valuep = lookup_extra_color(code);
2080 	if (valuep == 0) {
2081 	    switch (code) {
2082 #if OPT_HILITEMATCH
2083 		/*
2084 		 * FIXME - for now, implement global modeline as xcolor, but
2085 		 * later split out xcolor typing so modeline per-buffer can be
2086 		 * set.
2087 		 */
2088 	    case XCOLOR_MODELINE:
2089 		valuep = &(global_g_val(GVAL_MCOLOR));
2090 		break;
2091 #endif
2092 	    default:
2093 		break;
2094 	    }
2095 	}
2096 	if (valuep != 0 && (value = *valuep) != 0) {
2097 	    VIDEO_ATTR save_attr = (VIDEO_ATTR) value;
2098 	    const char *name;
2099 	    int bit = 1;
2100 	    int gap = 0;
2101 
2102 	    MK = DOT;
2103 	    if (value & VACOLOR) {
2104 		name = choice_to_name(&fsm_hilite_blist, VASPCOL | (value & VACOLOR));
2105 		bprintf("%s", name ? name : "color?");
2106 		value &= ~(VACOLOR | VASPCOL);
2107 		gap = (value != 0);
2108 	    }
2109 	    while (bit < (int) VACOLOR) {
2110 		if (bit & value) {
2111 		    name = choice_to_name(&fsm_videoattrs_blist, bit & value);
2112 		    if (!isEmpty(name)) {
2113 			if (gap)
2114 			    bputc('+');
2115 			bprintf("%s", name);
2116 			gap = 1;
2117 		    }
2118 		}
2119 		bit <<= 1;
2120 	    }
2121 
2122 	    videoattribute = save_attr;
2123 	    if (videoattribute != 0) {
2124 		REGIONSHAPE save_shape = regionshape;
2125 		regionshape = rgn_EXACT;
2126 		(void) attributeregion();
2127 		videoattribute = 0;
2128 		regionshape = save_shape;
2129 	    }
2130 	} else {
2131 	    bprintf("default");
2132 	}
2133     }
2134 }
2135 
2136 int
show_extra_colors(int f GCC_UNUSED,int n GCC_UNUSED)2137 show_extra_colors(int f GCC_UNUSED, int n GCC_UNUSED)
2138 {
2139     return liststuff(EXTRA_COLORS_BufName,
2140 		     FALSE, make_xcolor_list, 0, (void *) 0);
2141 }
2142 
2143 static int
xcolor_name_complete(DONE_ARGS)2144 xcolor_name_complete(DONE_ARGS)
2145 {
2146     return kbd_complete(PASS_DONE_ARGS,
2147 			(const char *) &fsm_xcolors_choices[0],
2148 			sizeof(fsm_xcolors_choices[0]));
2149 }
2150 
2151 static int
xcolor_full_complete(DONE_ARGS)2152 xcolor_full_complete(DONE_ARGS)
2153 {
2154     return kbd_complete(PASS_DONE_ARGS,
2155 			(const char *) &fsm_hilite_choices[0],
2156 			sizeof(fsm_hilite_choices[0]));
2157 }
2158 
2159 #if OPT_UPBUFF
2160 /* ARGSUSED */
2161 static int
update_xcolor_list(BUFFER * bp GCC_UNUSED)2162 update_xcolor_list(BUFFER *bp GCC_UNUSED)
2163 {
2164     return show_extra_colors(FALSE, 1);
2165 }
2166 
2167 static void
relist_xcolors(void)2168 relist_xcolors(void)
2169 {
2170     update_scratch(EXTRA_COLORS_BufName, update_xcolor_list);
2171 }
2172 #endif /* OPT_UPBUFF */
2173 
2174 /*
2175  * Lookup a symbol to see if it is one of vile's color/attribute names.
2176  */
2177 int
vl_lookup_color(const char * name)2178 vl_lookup_color(const char *name)
2179 {
2180     return choice_to_code(&fsm_hilite_blist, name, strlen(name));
2181 }
2182 
2183 /*
2184  * Lookup a symbol to see if it is one of vile's extended color names.
2185  */
2186 int
vl_lookup_xcolor(const char * name)2187 vl_lookup_xcolor(const char *name)
2188 {
2189     return choice_to_code(&fsm_xcolors_blist, name, strlen(name));
2190 }
2191 
2192 /*
2193  * C and C++ really differ for handling of enums.
2194  */
2195 #if defined(__cplusplus) || defined(__INTEL_COMPILER)
2196 static inline XCOLOR_CODES
XColorOf(int value)2197 XColorOf(int value)
2198 {
2199     return (XCOLOR_CODES) value;
2200 }
2201 #else
2202 #define XColorOf(value) value
2203 #endif
2204 
2205 /*
2206  * Prompt for an extended color name,
2207  * and zero or one color
2208  * added to zero or more video attributes.
2209  *
2210  * Examples:
2211  *	set-extra-color modeline reverse
2212  *	set-extra-color regex underline+red
2213  *
2214  * FIXME - make this work with command-history.
2215  * FIXME - want to be able to undo levels of the edit, back over '+'.
2216  */
2217 int
set_extra_colors(int f GCC_UNUSED,int n GCC_UNUSED)2218 set_extra_colors(int f GCC_UNUSED, int n GCC_UNUSED)
2219 {
2220     int status = FALSE;
2221     char prompt[NSTRING];
2222     char reply[NSTRING];
2223     XCOLOR_CODES code, code2;
2224     int *valuep;
2225     int value;
2226     int count = 0;
2227 
2228     /* prompt for the extended color name */
2229     *reply = EOS;
2230     status = kbd_string("Extended color name: ",
2231 			reply, sizeof(reply), '=',
2232 			KBD_NORMAL, xcolor_name_complete);
2233     if (status == TRUE) {
2234 	code = XColorOf(vl_lookup_xcolor(reply));
2235 	valuep = lookup_extra_color(code);
2236 	value = 0;
2237 	sprintf(prompt, "Color+attributes(%.*s) ",
2238 		(int) sizeof(prompt) - 24,
2239 		reply);
2240 	/* prompt for the color+attributes */
2241 	for (;;) {
2242 	    *reply = EOS;
2243 	    status = kbd_string(prompt,
2244 				reply, sizeof(reply), '+',
2245 				KBD_NORMAL, xcolor_full_complete);
2246 	    if (status == TRUE) {
2247 		code2 = XColorOf(choice_to_code(&fsm_hilite_blist,
2248 						reply, strlen(reply)));
2249 
2250 		/*
2251 		 * FIXME:  We could be fancier and only prompt for attributes
2252 		 * once a color is given, but it may be friendlier to just use
2253 		 * the last color given.
2254 		 */
2255 		++count;
2256 		if (code2 & VACOLOR)
2257 		    value &= ~VACOLOR;
2258 		value |= (int) ((unsigned) code2 & (unsigned) (~VASPCOL));
2259 
2260 		strcat(prompt, reply);
2261 		if (end_string() == '+') {
2262 		    strcat(prompt, "+");
2263 		} else {
2264 		    break;
2265 		}
2266 	    } else {
2267 		break;
2268 	    }
2269 	}
2270 
2271 	if (!status || !count) {
2272 	    ;			/* no value set */
2273 	} else if (valuep != 0) {
2274 	    if (value != *valuep) {
2275 		set_winflags(TRUE, WFHARD | WFCOLR);
2276 		*valuep = value;
2277 	    }
2278 	} else {
2279 	    switch (code) {
2280 #if OPT_HILITEMATCH
2281 		/*
2282 		 * FIXME - for now, implement global modeline as xcolor, but
2283 		 * later split out xcolor typing so modeline per-buffer can be
2284 		 * set.
2285 		 */
2286 	    case XCOLOR_MODELINE:
2287 		set_global_g_val(GVAL_MCOLOR, value);
2288 		chgd_hilite(curbp, (VALARGS *) 0, TRUE, FALSE);
2289 		break;
2290 #endif
2291 	    default:
2292 		mlforce("BUG: unimplemented %s", prompt);
2293 		break;
2294 	    }
2295 	}
2296     }
2297 
2298     if (status == TRUE)
2299 	relist_xcolors();
2300 
2301     return status;
2302 }
2303 #endif
2304 
2305 #if OPT_SHOW_COLORS
2306 
2307 #if OPT_COLOR_CHOICES
2308 static const char *
lookup_color_code(int code)2309 lookup_color_code(int code)
2310 {
2311     const char *rc = 0;
2312     int j;
2313     const FSM_CHOICES *the_colors = name_to_choices(s_fcolor)->choices;
2314 
2315     for (j = 0; the_colors[j].choice_name != 0; j++) {
2316 	if (code == the_colors[j].choice_code) {
2317 	    rc = the_colors[j].choice_name;
2318 	    break;
2319 	}
2320     }
2321     return rc;
2322 }
2323 #endif
2324 
2325 int
is_color_code(int n)2326 is_color_code(int n)
2327 {
2328     int rc;
2329 #if OPT_COLOR_CHOICES
2330     rc = (lookup_color_code(n) != 0);
2331 #else
2332     rc = (c >= C_BLACK && n < ncolors);
2333 #endif
2334     return rc;
2335 }
2336 
2337 const char *
get_color_name(int n)2338 get_color_name(int n)
2339 {
2340     static char temp[80];
2341     const char *rc = 0;
2342 
2343 #if OPT_COLOR_CHOICES
2344     if ((rc = lookup_color_code(n)) == 0)
2345 #endif
2346     {
2347 	lsprintf(temp, "color #%d", n);
2348 	rc = temp;
2349     }
2350     return rc;
2351 }
2352 #endif
2353 
2354 	/* Report mode that cannot be changed */
2355 /*ARGSUSED*/
2356 int
chgd_disabled(BUFFER * bp GCC_UNUSED,VALARGS * args,int glob_vals GCC_UNUSED,int testing GCC_UNUSED)2357 chgd_disabled(BUFFER *bp GCC_UNUSED,
2358 	      VALARGS * args,
2359 	      int glob_vals GCC_UNUSED,
2360 	      int testing GCC_UNUSED)
2361 {
2362     mlforce("[Cannot change \"%s\" ]", args->names->name);
2363     return FALSE;
2364 }
2365 
2366 /* Change "fences" mode */
2367 /*ARGSUSED*/
2368 int
chgd_fences(BUFFER * bp GCC_UNUSED,VALARGS * args,int glob_vals GCC_UNUSED,int testing)2369 chgd_fences(BUFFER *bp GCC_UNUSED, VALARGS * args, int glob_vals GCC_UNUSED, int testing)
2370 {
2371     if (!testing) {
2372 	/* was even number of fence pairs specified? */
2373 	char *value = args->local->vp->p;
2374 	size_t len = strlen(value);
2375 
2376 	if (len & 1) {
2377 	    value[len - 1] = EOS;
2378 	    mlwrite("[Fence-pairs not in pairs:  truncating to \"%s\"",
2379 		    value);
2380 	    return FALSE;
2381 	}
2382     }
2383     return TRUE;
2384 }
2385 
2386 /* Change a "major" mode (one that we cannot use in majormodes) */
2387 int
chgd_major(BUFFER * bp,VALARGS * args,int glob_vals,int testing)2388 chgd_major(BUFFER *bp, VALARGS * args, int glob_vals, int testing)
2389 {
2390     /* prevent major changes for scratch-buffers */
2391     if (testing) {
2392 	if (!glob_vals) {
2393 	    if (b_is_scratch(bp))
2394 		return chgd_disabled(bp, args, glob_vals, testing);
2395 	}
2396     } else {
2397 	set_bufflags(glob_vals, WFMODE);
2398     }
2399     return TRUE;
2400 }
2401 
2402 /* Change the "undoable" mode (this is also a major change) */
2403 int
chgd_undoable(BUFFER * bp,VALARGS * args,int glob_vals,int testing)2404 chgd_undoable(BUFFER *bp, VALARGS * args, int glob_vals, int testing)
2405 {
2406     if (chgd_major(bp, args, glob_vals, testing)) {
2407 	if (!testing
2408 	    && !(args->local->v.i))
2409 	    freeundostacks(bp, TRUE);
2410 	return TRUE;
2411     }
2412     return FALSE;
2413 }
2414 
2415 /* Change a mode that affects the window(s) on the buffer */
2416 int
chgd_win_mode(BUFFER * bp,VALARGS * args,int glob_vals,int testing)2417 chgd_win_mode(BUFFER *bp, VALARGS * args, int glob_vals, int testing)
2418 {
2419     if (testing) {
2420 	if (!chgd_major(bp, args, glob_vals, testing))
2421 	    return FALSE;
2422 	return chgd_window(bp, args, glob_vals, testing);
2423     }
2424 
2425     set_winflags(glob_vals, WFHARD | WFMODE);
2426     return TRUE;
2427 }
2428 
2429 /* Change the DOS-mode on the buffer */
2430 int
chgd_dos_mode(BUFFER * bp,VALARGS * args,int glob_vals,int testing)2431 chgd_dos_mode(BUFFER *bp, VALARGS * args, int glob_vals, int testing)
2432 {
2433     int rc = TRUE;
2434 
2435     if (testing) {
2436 	rc = chgd_win_mode(bp, args, glob_vals, testing);
2437     } else {
2438 	if (!glob_vals) {
2439 	    if (args->local->vp->i) {
2440 		rc = set_rs_crlf(FALSE, 1);
2441 	    } else {
2442 		rc = set_rs_lf(FALSE, 1);
2443 	    }
2444 	}
2445 	set_winflags(glob_vals, WFHARD | WFMODE);
2446     }
2447     return rc;
2448 }
2449 
2450 int
chgd_percent(BUFFER * bp GCC_UNUSED,VALARGS * args,int glob_vals GCC_UNUSED,int testing GCC_UNUSED)2451 chgd_percent(BUFFER *bp GCC_UNUSED, VALARGS * args, int glob_vals
2452 	     GCC_UNUSED, int testing GCC_UNUSED)
2453 {
2454     int rc = TRUE;
2455     if ((args->local->vp->i < 0) || (args->local->vp->i > 100)) {
2456 	rc = FALSE;
2457     }
2458     return rc;
2459 }
2460 
2461 void
set_record_sep(BUFFER * bp,RECORD_SEP code)2462 set_record_sep(BUFFER *bp, RECORD_SEP code)
2463 {
2464     const char *recordsep = "\n";
2465 
2466     if (code == RS_DEFAULT)
2467 	code = RS_SYS_DEFAULT;
2468 
2469     switch (code) {
2470     case RS_AUTO:
2471     case RS_DEFAULT:
2472 	/* see above */
2473 	break;
2474     case RS_LF:
2475 	/* see above */
2476 	break;
2477     case RS_CRLF:
2478 	recordsep = "\r\n";
2479 	break;
2480     case RS_CR:
2481 	recordsep = "\r";
2482 	break;
2483     }
2484     (bp)->b_recordsep_str = recordsep;
2485     (bp)->b_recordsep_len = (int) strlen(get_record_sep(bp));
2486 
2487     set_local_b_val(bp, MDDOS, (code == RS_CRLF));
2488     set_local_b_val(bp, VAL_RECORD_SEP, code);
2489     b_clr_counted(bp);
2490     (void) bsizes(bp);
2491     relist_settings();
2492     updatelistbuffers();
2493     set_winflags(TRUE, WFMODE);
2494 }
2495 
2496 /* Change the record separator */
2497 int
chgd_rs(BUFFER * bp,VALARGS * args,int glob_vals,int testing)2498 chgd_rs(BUFFER *bp, VALARGS * args, int glob_vals, int testing)
2499 {
2500     if (!testing) {
2501 	if (bp == 0)
2502 	    return FALSE;
2503 	if (args->local->vp->i == RS_AUTO && !glob_vals) {
2504 	    args->local->vp->i = RS_DEFAULT;
2505 	}
2506 	set_record_sep(bp, (RECORD_SEP) args->local->vp->i);
2507     }
2508 
2509     set_winflags(TRUE, WFMODE);
2510     return chgd_win_mode(bp, args, glob_vals, testing);
2511 }
2512 
2513 /* Change something on the mode/status line */
2514 /*ARGSUSED*/
2515 int
chgd_status(BUFFER * bp GCC_UNUSED,VALARGS * args GCC_UNUSED,int glob_vals,int testing)2516 chgd_status(BUFFER *bp GCC_UNUSED, VALARGS * args GCC_UNUSED, int glob_vals, int testing)
2517 {
2518     if (!testing) {
2519 	set_winflags(glob_vals, WFSTAT);
2520     }
2521     return TRUE;
2522 }
2523 
2524 #if OPT_CURTOKENS
2525 static int
valid_rexp(REGEXVAL * r)2526 valid_rexp(REGEXVAL * r)
2527 {
2528     return (r != 0 && r->pat != 0);
2529 }
2530 
2531 static int
update_regexval(struct VAL * val,const char * pattern,int magic)2532 update_regexval(struct VAL *val, const char *pattern, int magic)
2533 {
2534     int result = TRUE;
2535     union V *vp = val->vp;
2536 
2537     vp->r = free_regexval(vp->r);
2538     vp->r = new_regexval(pattern, magic);
2539     if (vp->r == 0) {
2540 	vp->r = new_regexval("", TRUE);
2541 	result = FALSE;
2542     }
2543     return result;
2544 }
2545 
2546 /*
2547  * Maintain the $buf-fname-expr variable, which is the combination of the
2548  * bufname-expr and pathname-expr modes.
2549  *
2550  * Yes, we could add another mode - but since the value is readonly and
2551  * derived, it does not really fit as a mode.  It is stored in BUFFER as a
2552  * struct VAL so we can treat the value as a local/global mode.
2553  */
2554 void
set_buf_fname_expr(BUFFER * bp)2555 set_buf_fname_expr(BUFFER *bp)
2556 {
2557     TBUFF *combined = 0;
2558     REGEXVAL *bufname_expr = ((bp != 0)
2559 			      ? b_val_rexp(bp, VAL_BUFNAME_EXPR)
2560 			      : global_b_val_rexp(VAL_BUFNAME_EXPR));
2561     REGEXVAL *pathname_expr = ((bp != 0)
2562 			       ? b_val_rexp(bp, VAL_PATHNAME_EXPR)
2563 			       : global_b_val_rexp(VAL_PATHNAME_EXPR));
2564 
2565     if ((bp == 0
2566 	 || b_val(bp, VAL_CURSOR_TOKENS) != CT_CCLASS)
2567 	&& valid_rexp(bufname_expr)
2568 	&& valid_rexp(pathname_expr)
2569 	&& tb_sappend(&combined, "\\(")
2570 	&& tb_sappend(&combined, bufname_expr->pat)
2571 	&& tb_sappend(&combined, "\\|")
2572 	&& tb_sappend(&combined, pathname_expr->pat)
2573 	&& tb_sappend(&combined, "\\)")
2574 	&& tb_append(&combined, EOS)) {
2575 	char *pattern = tb_values(combined);
2576 	struct VAL *global_expr = &buf_fname_expr;
2577 	struct VAL *local_expr;
2578 	int local = FALSE;
2579 
2580 	TRACE(("set_buf_fname_expr(%s)\n", pattern));
2581 	if (bp != 0) {
2582 	    local_expr = &(bp->buf_fname_expr);
2583 
2584 	    if (is_local_b_val(bp, VAL_BUFNAME_EXPR)
2585 		|| is_local_b_val(bp, VAL_PATHNAME_EXPR)) {
2586 		local = TRUE;
2587 		local_expr->vp = &(local_expr->v);
2588 		update_regexval(local_expr, pattern, TRUE);
2589 	    } else {
2590 		local_expr->vp = &(global_expr->v);
2591 	    }
2592 	}
2593 	if (!local) {
2594 	    global_expr->vp = &(global_expr->v);
2595 	    update_regexval(global_expr, pattern, TRUE);
2596 	}
2597     }
2598     tb_free(&combined);
2599 }
2600 
2601 REGEXVAL *
get_buf_fname_expr(BUFFER * bp)2602 get_buf_fname_expr(BUFFER *bp)
2603 {
2604     REGEXVAL *result = 0;
2605     if (bp->buf_fname_expr.vp != 0)
2606 	result = bp->buf_fname_expr.vp->r;
2607     else
2608 	result = buf_fname_expr.vp->r;
2609     return result;
2610 }
2611 
2612 int
chgd_curtokens(BUFFER * bp,VALARGS * args GCC_UNUSED,int glob_vals GCC_UNUSED,int testing)2613 chgd_curtokens(BUFFER *bp,
2614 	       VALARGS * args GCC_UNUSED,
2615 	       int glob_vals GCC_UNUSED,
2616 	       int testing)
2617 {
2618     if (!testing)
2619 	set_buf_fname_expr(bp);
2620     return TRUE;
2621 }
2622 #endif
2623 
2624 #if OPT_TITLE
2625 /* Changed swap-title */
2626 int
chgd_swaptitle(BUFFER * bp GCC_UNUSED,VALARGS * args GCC_UNUSED,int glob_vals GCC_UNUSED,int testing)2627 chgd_swaptitle(BUFFER *bp GCC_UNUSED,
2628 	       VALARGS * args GCC_UNUSED,
2629 	       int glob_vals GCC_UNUSED,
2630 	       int testing)
2631 {
2632     if (!testing)
2633 	set_editor_title();
2634     return TRUE;
2635 }
2636 #endif
2637 
2638 #if OPT_FINDPATH
2639 /*
2640  * user changed find-cfg mode
2641  *
2642  * Strictly speaking, there is no reason to require that a chgd() function
2643  * track changes to find-cfg mode.  However, this routine does allow the
2644  * editor to flag incorrect syntax before a shell command is initiated.
2645  */
2646 int
chgd_find_cfg(BUFFER * bp GCC_UNUSED,VALARGS * args,int glob_vals GCC_UNUSED,int testing)2647 chgd_find_cfg(BUFFER *bp GCC_UNUSED,
2648 	      VALARGS * args,
2649 	      int glob_vals GCC_UNUSED,
2650 	      int testing)
2651 {
2652     int rc = TRUE;
2653     FINDCFG unused;
2654 
2655     if (!testing)
2656 	rc = parse_findcfg_mode(&unused, args->global->vp->p);
2657     return (rc);
2658 }
2659 #endif
2660 
2661 /* Change a mode that affects the windows on the buffer */
2662 /*ARGSUSED*/
2663 int
chgd_window(BUFFER * bp GCC_UNUSED,VALARGS * args GCC_UNUSED,int glob_vals,int testing)2664 chgd_window(BUFFER *bp GCC_UNUSED, VALARGS * args GCC_UNUSED, int glob_vals, int testing)
2665 {
2666     if (!testing) {
2667 	set_winflags(glob_vals, WFHARD);
2668     }
2669     return TRUE;
2670 }
2671 
2672 /* if unique-buffers is turned on, make sure all buffers that
2673    can have a valid fuid */
2674 /*ARGSUSED*/
2675 int
chgd_uniqbuf(BUFFER * bp GCC_UNUSED,VALARGS * args GCC_UNUSED,int glob_vals GCC_UNUSED,int testing)2676 chgd_uniqbuf(BUFFER *bp GCC_UNUSED,
2677 	     VALARGS * args GCC_UNUSED,
2678 	     int glob_vals GCC_UNUSED,
2679 	     int testing)
2680 {
2681     if (!testing) {
2682 	if (global_g_val(GMDUNIQ_BUFS)) {
2683 	    FUID fuid;
2684 	    BUFFER *bp2;
2685 
2686 	    for_each_buffer(bp2) {
2687 		if (bp2->b_fname != 0
2688 		    && !isInternalName(bp2->b_fname)
2689 		    && fileuid_get(bp2->b_fname, &fuid)) {
2690 		    fileuid_set(bp2, &fuid);
2691 		}
2692 	    }
2693 	}
2694     }
2695     return TRUE;
2696 }
2697 
2698 /* Change the working mode */
2699 #if OPT_WORKING
2700 /*ARGSUSED*/
2701 int
chgd_working(BUFFER * bp GCC_UNUSED,VALARGS * args GCC_UNUSED,int glob_vals,int testing GCC_UNUSED)2702 chgd_working(BUFFER *bp GCC_UNUSED,
2703 	     VALARGS * args GCC_UNUSED,
2704 	     int glob_vals,
2705 	     int testing GCC_UNUSED)
2706 {
2707     if (glob_vals)
2708 	imworking(0);
2709     return TRUE;
2710 }
2711 #endif
2712 
2713 /* Change the xterm-mouse mode */
2714 /*ARGSUSED*/
2715 int
chgd_xterm(BUFFER * bp GCC_UNUSED,VALARGS * args GCC_UNUSED,int glob_vals GCC_UNUSED,int testing GCC_UNUSED)2716 chgd_xterm(BUFFER *bp GCC_UNUSED,
2717 	   VALARGS * args GCC_UNUSED,
2718 	   int glob_vals GCC_UNUSED,
2719 	   int testing GCC_UNUSED)
2720 {
2721 #if OPT_XTERM
2722     if (glob_vals && !testing) {
2723 	int new_state = global_g_val(GMDXTERM_MOUSE);
2724 	set_global_g_val(GMDXTERM_MOUSE, !new_state);
2725 	term.kclose();
2726 	set_global_g_val(GMDXTERM_MOUSE, new_state);
2727 	term.kopen();
2728 	vile_refresh(FALSE, 0);
2729     }
2730 #endif
2731     return TRUE;
2732 }
2733 
2734 /* Change the xterm-fkeys mode */
2735 /*ARGSUSED*/
2736 int
chgd_xtermkeys(BUFFER * bp GCC_UNUSED,VALARGS * args GCC_UNUSED,int glob_vals GCC_UNUSED,int testing GCC_UNUSED)2737 chgd_xtermkeys(BUFFER *bp GCC_UNUSED,
2738 	       VALARGS * args GCC_UNUSED,
2739 	       int glob_vals GCC_UNUSED,
2740 	       int testing GCC_UNUSED)
2741 {
2742 #if DISP_TERMCAP || DISP_CURSES
2743     if (glob_vals && !testing) {
2744 	tcap_init_fkeys();
2745     }
2746 #endif
2747     return TRUE;
2748 }
2749 
2750 /* Change the mouse mode */
2751 #ifdef GMDMOUSE
2752 /*ARGSUSED*/
2753 int
chgd_mouse(BUFFER * bp,VALARGS * args GCC_UNUSED,int glob_vals,int testing GCC_UNUSED)2754 chgd_mouse(BUFFER *bp, VALARGS * args GCC_UNUSED, int glob_vals, int testing GCC_UNUSED)
2755 {
2756     if (glob_vals) {
2757 	int new_state = global_g_val(GMDMOUSE);
2758 	set_global_g_val(GMDMOUSE, TRUE);
2759 	if (!new_state)
2760 	    term.kclose();
2761 	else
2762 	    term.kopen();
2763 	set_global_g_val(GMDMOUSE, new_state);
2764     }
2765     return TRUE;
2766 }
2767 #endif
2768 
2769 /* Change a mode that affects the search-string highlighting */
2770 /*ARGSUSED*/
2771 int
chgd_hilite(BUFFER * bp,VALARGS * args GCC_UNUSED,int glob_vals GCC_UNUSED,int testing)2772 chgd_hilite(BUFFER *bp, VALARGS * args GCC_UNUSED, int glob_vals GCC_UNUSED, int testing)
2773 {
2774     (void) bp;
2775 
2776     if (!testing) {
2777 #if OPT_HILITEMATCH
2778 	if (bp->b_highlight & HILITE_ON)
2779 	    bp->b_highlight |= HILITE_DIRTY;
2780 	if (bp == curbp)	/* FIXME: attrib_matches only does curbp */
2781 	    attrib_matches();
2782 #endif
2783 #if OPT_COLOR
2784 	set_winflags(glob_vals, WFHARD | WFCOLR);
2785 	need_update = TRUE;
2786 #endif
2787     }
2788     return TRUE;
2789 }
2790 
2791 #if SYS_WINNT
2792 int
chgd_rcntfiles(BUFFER * bp GCC_UNUSED,VALARGS * args,int glob_vals GCC_UNUSED,int testing)2793 chgd_rcntfiles(BUFFER *bp GCC_UNUSED,
2794 	       VALARGS * args,
2795 	       int glob_vals GCC_UNUSED,
2796 	       int testing)
2797 {
2798     if (!testing) {
2799 	int nfiles = args->global->v.i;
2800 
2801 	if (nfiles < 0 || nfiles > MAX_RECENT_FILES)
2802 	    return (FALSE);
2803     }
2804     return (TRUE);
2805 }
2806 
2807 int
chgd_rcntfldrs(BUFFER * bp GCC_UNUSED,VALARGS * args,int glob_vals GCC_UNUSED,int testing)2808 chgd_rcntfldrs(BUFFER *bp GCC_UNUSED,
2809 	       VALARGS * args,
2810 	       int glob_vals GCC_UNUSED,
2811 	       int testing)
2812 {
2813     if (!testing) {
2814 	int nfolders = args->global->v.i;
2815 
2816 	if (nfolders < 0 || nfolders > MAX_RECENT_FLDRS)
2817 	    return (FALSE);
2818     }
2819     return (TRUE);
2820 }
2821 #endif /* SYS_WINNT */
2822 
2823 /*--------------------------------------------------------------------------*/
2824 
2825 #if OPT_EVAL || OPT_MAJORMODE
2826 /*
2827  * Test for mode-names that we'll not show in the variable name-completion.
2828  */
2829 int
is_varmode(const char * name)2830 is_varmode(const char *name)
2831 {
2832     return (!noPrefix(name)
2833 	    && strcmp(name, "all"));
2834 }
2835 
2836 /*
2837  * Returns the current number of items in the list of modes
2838  */
2839 static int
count_modes(void)2840 count_modes(void)
2841 {
2842     init_my_mode_list();
2843     return blist_count(&blist_my_mode_list);
2844 }
2845 
2846 /*
2847  * Returns true if the given pointer is in all_modes[].  We use this to check
2848  * whether a name is in the original const array or not, so we can free it.
2849  */
2850 static int
in_all_modes(const char * name)2851 in_all_modes(const char *name)
2852 {
2853     unsigned n;
2854     for (n = 0; all_modes[n] != 0; n++) {
2855 	if (all_modes[n] == name)
2856 	    return TRUE;
2857     }
2858     return FALSE;
2859 }
2860 
2861 static const char **
const_varmodes(char ** value)2862 const_varmodes(char **value)
2863 {
2864     return (const char **) value;
2865 }
2866 
2867 static void
fill_my_varmodes(void)2868 fill_my_varmodes(void)
2869 {
2870     const char *const *s;
2871     const char **d;
2872 
2873     if (my_varmodes != 0 && my_mode_list != 0) {
2874 	for (s = my_mode_list,
2875 	     d = const_varmodes(my_varmodes); (*d = *s) != 0; s++) {
2876 	    if (is_varmode(*d)) {
2877 		d++;
2878 	    }
2879 	}
2880     }
2881 }
2882 
2883 static size_t
need_my_varmodes(size_t actual)2884 need_my_varmodes(size_t actual)
2885 {
2886     return (actual | 31);
2887 }
2888 
2889 static void
free_my_varmodes(void)2890 free_my_varmodes(void)
2891 {
2892     FreeAndNull(my_varmodes);
2893 }
2894 
2895 /*
2896  * The my_varmodes[] data is an index into my_mode_list.
2897  */
2898 static void
check_my_varmodes(size_t actual)2899 check_my_varmodes(size_t actual)
2900 {
2901     if (my_varmodes != 0) {
2902 	size_t need = need_my_varmodes(actual);
2903 	if (need > size_my_varmodes) {
2904 	    free_my_varmodes();
2905 	}
2906     }
2907 }
2908 
2909 /*
2910  * Return a list of only the modes that can be set with ":setv", ignoring
2911  * artifacts such as "all".
2912  */
2913 const char *const *
list_of_modes(void)2914 list_of_modes(void)
2915 {
2916     if (my_varmodes == 0) {
2917 	size_t n;
2918 
2919 	beginDisplay();
2920 	n = need_my_varmodes((size_t) count_modes()) + 1;
2921 	my_varmodes = typeallocn(char *, n);
2922 	size_my_varmodes = n;
2923 	endofDisplay();
2924 
2925 	fill_my_varmodes();
2926     }
2927     return (const char *const *) my_varmodes;
2928 }
2929 #endif /* OPT_EVAL || OPT_MAJORMODE */
2930 
2931 /*--------------------------------------------------------------------------*/
2932 
2933 #if OPT_MAJORMODE
2934 static int
is_identifier(const char * name)2935 is_identifier(const char *name)
2936 {
2937     if (name != 0) {
2938 	int first = TRUE;
2939 
2940 	while (*name != EOS) {
2941 	    if (first) {
2942 		if (!isAlpha(*name))
2943 		    return FALSE;
2944 		first = FALSE;
2945 	    } else if (!isident(*name))
2946 		return FALSE;
2947 	    name++;
2948 	}
2949 	return TRUE;
2950     }
2951     return FALSE;
2952 }
2953 
2954 static int
ok_submode(const char * name)2955 ok_submode(const char *name)
2956 {
2957     /* like is_varmode, but allow "no" prefix */
2958     return strcmp(name, "all") != 0 ? TRUE : FALSE;
2959 }
2960 
2961 /* format the name of a majormode's qualifier */
2962 static char *
per_major(char * dst,const char * majr,size_t code,int brief)2963 per_major(char *dst, const char *majr, size_t code, int brief)
2964 {
2965     if (brief) {
2966 	if (!strcmp(m_valnames[code].shortname, "X")) {
2967 	    *dst = EOS;
2968 	} else {
2969 	    (void) lsprintf(dst, "%s%s", majr, m_valnames[code].shortname);
2970 	}
2971     } else {
2972 	(void) lsprintf(dst, "%s-%s", majr, m_valnames[code].name);
2973     }
2974     return dst;
2975 }
2976 
2977 /* format the name of a majormode's submode */
2978 static char *
per_submode(char * dst,const char * majr,int code,int brief)2979 per_submode(char *dst, const char *majr, int code, int brief)
2980 {
2981     if (brief) {
2982 	if (!strcmp(b_valnames[code].shortname, "X")) {
2983 	    *dst = EOS;
2984 	    return 0;
2985 	}
2986 	(void) lsprintf(dst, "%s%s", majr, b_valnames[code].shortname);
2987     } else {
2988 	(void) lsprintf(dst, "%s-%s", majr, b_valnames[code].name);
2989     }
2990     return dst;
2991 }
2992 
2993 static char *TheMajor;
2994 
2995 static const char *
ModeName(const char * name)2996 ModeName(const char *name)
2997 {
2998     if (TheMajor != 0) {
2999 	static char *dst;
3000 
3001 	beginDisplay();
3002 	if (dst != 0)
3003 	    free(dst);
3004 	dst = typeallocn(char, strlen(TheMajor) + strlen(name) + 3);
3005 	endofDisplay();
3006 
3007 	if (dst != 0) {
3008 	    (void) lsprintf(dst, "%s-%s", TheMajor, name);
3009 	    return dst;
3010 	}
3011     }
3012     return name;
3013 }
3014 
3015 /* format the name of a majormode */
3016 static char *
majorname(char * dst,const char * majr,int flag)3017 majorname(char *dst, const char *majr, int flag)
3018 {
3019     (void) lsprintf(dst, "%s%s" MAJOR_SUFFIX, flag ? "" : NO_PREFIX, majr);
3020     return dst;
3021 }
3022 
3023 /*
3024  * Returns the current number of items in the list of modes
3025  */
3026 static unsigned
count_majormodes(void)3027 count_majormodes(void)
3028 {
3029     unsigned n = 0;
3030 
3031     if (my_majormodes != 0) {
3032 	n = (unsigned) blist_count(&majormode_blist);
3033     }
3034     return n;
3035 }
3036 
3037 static int
get_mm_number(int n,int m)3038 get_mm_number(int n, int m)
3039 {
3040     int result = 0;
3041 
3042     if (my_majormodes[n].data != 0) {
3043 	struct VAL *mv = my_majormodes[n].data->mm.mv;
3044 
3045 	if (mv[m].vp->i != 0) {
3046 	    TRACE(("get_mm_number(%s) %d\n",
3047 		   my_majormodes[n].shortname,
3048 		   mv[m].vp->i));
3049 	    result = mv[m].vp->i;
3050 	}
3051     }
3052     return result;
3053 }
3054 
3055 /*
3056  * Returns the regular expression for the given indices, checking that the
3057  * pattern is non-null.
3058  */
3059 static regexp *
get_mm_rexp(int n,int m)3060 get_mm_rexp(int n, int m)
3061 {
3062     regexp *result = 0;
3063 
3064     if (my_majormodes[n].data != 0) {
3065 	struct VAL *mv = my_majormodes[n].data->mm.mv;
3066 
3067 	if (mv[m].vp != 0
3068 	    && mv[m].vp->r != 0
3069 	    && mv[m].vp->r->pat != 0
3070 	    && mv[m].vp->r->pat[0] != 0
3071 	    && mv[m].vp->r->reg != 0) {
3072 	    TRACE2(("get_mm_rexp(%s) %s\n",
3073 		    my_majormodes[n].shortname,
3074 		    mv[m].vp->r->pat));
3075 	    result = mv[m].vp->r->reg;
3076 	}
3077     }
3078     return result;
3079 }
3080 
3081 static char *
get_mm_string(int n,int m)3082 get_mm_string(int n, int m)
3083 {
3084     char *result = 0;
3085 
3086     if (my_majormodes[n].data != 0) {
3087 	struct VAL *mv = my_majormodes[n].data->mm.mv;
3088 
3089 	if (!isEmpty(mv[m].vp->p)) {
3090 	    TRACE2(("get_mm_string(%s) %s\n",
3091 		    my_majormodes[n].shortname,
3092 		    mv[m].vp->p));
3093 	    result = mv[m].vp->p;
3094 	}
3095     }
3096     return result;
3097 }
3098 
3099 #define GetMmString(n, mode) get_mm_string(majormodes_order[n], mode)
3100 
3101 static int
find_majormode_order(int mm)3102 find_majormode_order(int mm)
3103 {
3104     int found = -1;
3105     int n;
3106 
3107     if (majormodes_order != 0) {
3108 	for (n = 0; majormodes_order[n] >= 0; n++) {
3109 	    if (majormodes_order[n] == mm) {
3110 		found = n;
3111 		break;
3112 	    }
3113 	}
3114     }
3115     return found;
3116 }
3117 
3118 #if OPT_TRACE
3119 static void
show_majormode_order(const char * tag)3120 show_majormode_order(const char *tag)
3121 {
3122     static TBUFF *order;
3123 
3124     int n;
3125     if (majormodes_order != 0) {
3126 	tb_scopy(&order, "order ");
3127 	tb_sappend0(&order, tag);
3128 	for (n = 0; majormodes_order[n] >= 0; n++) {
3129 	    tb_sappend0(&order, " ");
3130 	    tb_sappend0(&order, mmShortName(n));
3131 	}
3132 	TRACE(("%s\n", tb_values(order)));
3133     }
3134 }
3135 #else
3136 #define show_majormode_order(tag)	/* nothing */
3137 #endif
3138 
3139 static int
find_mm_by_shortname(const char * s)3140 find_mm_by_shortname(const char *s)
3141 {
3142     const char *t;
3143     const char *told = "";
3144     int k;
3145     int kk;
3146     int found = -1;
3147 
3148     if (majormodes_order != 0) {
3149 	for (k = 0; (kk = majormodes_order[k]) >= 0; k++) {
3150 	    if ((t = my_majormodes[kk].shortname) != 0
3151 		&& strcmp(t, s) <= 0
3152 		&& (found < 0 || strcmp(told, t) < 0)) {
3153 		found = k;
3154 		told = t;
3155 	    }
3156 	}
3157     }
3158     return found;
3159 }
3160 
3161 #if OPT_TRACE
3162 static void
check_majormode_order(void)3163 check_majormode_order(void)
3164 {
3165     int j;
3166 
3167     for (j = 0; majormodes_order[j] >= 0; j++) {
3168 	const char *t = my_majormodes[majormodes_order[j]].shortname;
3169 	const char *s;
3170 	int found;
3171 
3172 	if ((s = GetMmString(j, MVAL_BEFORE)) != 0) {
3173 	    found = find_mm_by_shortname(s);
3174 	    if (found < (int) j)
3175 		TRACE(("failed check %s before %s: %d vs %d\n",
3176 		       t, s, j, found));
3177 	}
3178 	if ((s = GetMmString(j, MVAL_AFTER)) != 0) {
3179 	    found = find_mm_by_shortname(s);
3180 	    if (found > (int) j)
3181 		TRACE(("failed check %s after %s: %d vs %d\n",
3182 		       t, s, j, found));
3183 	}
3184     }
3185     TRACE(("checked %d majormodes\n", j));
3186 }
3187 
3188 #else
3189 #define check_majormode_order()	/* nothing */
3190 #endif
3191 
3192 static int
put_majormode_before(unsigned j,const char * s)3193 put_majormode_before(unsigned j, const char *s)
3194 {
3195     if (majormodes_order != 0) {
3196 	int kk;
3197 	int found;
3198 
3199 	TRACE(("put_majormode_before(%d:%s, %s)\n",
3200 	       j,
3201 	       mmShortName(j),
3202 	       s));
3203 
3204 	found = find_mm_by_shortname(s);
3205 
3206 	if (found >= 0 && found < (int) j) {
3207 	    show_majormode_order("before");
3208 	    kk = majormodes_order[j];
3209 	    while (found < (int) j) {
3210 		majormodes_order[j] =
3211 		    majormodes_order[j - 1];
3212 		j--;
3213 	    }
3214 	    majormodes_order[j] = kk;
3215 	    show_majormode_order("after:");
3216 	} else if (found < 0) {
3217 	    TPRINTF(("cannot put %s before %s (not found)\n",
3218 		     mmShortName(j),
3219 		     s));
3220 	    TRACE(("...did not find %s\n", s));
3221 	}
3222 	TRACE(("->%d\n", j));
3223     }
3224     return (int) j;
3225 }
3226 
3227 static int
put_majormode_after(unsigned j,char * s)3228 put_majormode_after(unsigned j, char *s)
3229 {
3230     if (majormodes_order != 0) {
3231 	int kk;
3232 	int found;
3233 
3234 	TRACE(("put_majormode_after(%d:%s, %s)\n",
3235 	       j,
3236 	       mmShortName(j),
3237 	       s));
3238 
3239 	found = find_mm_by_shortname(s);
3240 
3241 	if (found >= 0 && found > (int) j) {
3242 	    show_majormode_order("before");
3243 	    kk = majormodes_order[j];
3244 	    while (found > (int) j) {
3245 		majormodes_order[j] =
3246 		    majormodes_order[j + 1];
3247 		j++;
3248 	    }
3249 	    majormodes_order[found] = kk;
3250 	    show_majormode_order("after:");
3251 	} else if (found < 0) {
3252 	    TPRINTF(("cannot put %s after %s (not found)\n",
3253 		     mmShortName(j),
3254 		     s));
3255 	    TRACE(("...did not find %s\n", s));
3256 	}
3257 	TRACE(("->%d\n", j));
3258     }
3259     return (int) j;
3260 }
3261 
3262 /*
3263  * Check for infinite loops which could occur if two majormodes listed each
3264  * other as "before".
3265  */
3266 static int
avoid_loop_before(int from,int to)3267 avoid_loop_before(int from, int to)
3268 {
3269     char *first = mmShortName(from);
3270     char *find;
3271     char *test;
3272     int j, k;
3273     int mode = MVAL_BEFORE;
3274 
3275     for (j = from; j <= to; ++j) {
3276 	find = GetMmString(j, mode);
3277 	if (find != 0) {
3278 	    for (k = j + 1; k <= to; ++k) {
3279 		test = GetMmString(k, mode);
3280 		if (test != 0
3281 		    && !strcmp(test, first)) {
3282 		    TPRINTF(("Avoid order-loop between \"%s\" and \"%s\"\n",
3283 			     first, mmShortName(k)));
3284 		    from = k + 1;
3285 		    j = from;
3286 		}
3287 	    }
3288 	}
3289     }
3290     return from;
3291 }
3292 
3293 /*
3294  * Make an ordered index into my_majormodes[], accounting for "before" and
3295  * "after" qualifiers.
3296  */
3297 static void
compute_majormodes_order(void)3298 compute_majormodes_order(void)
3299 {
3300     char *s;
3301     UINT need = count_majormodes();
3302     UINT want;
3303     UINT j;
3304     int jj;
3305     static UINT have;
3306 
3307     TRACE((T_CALLED "compute_majormodes_order(%d)\n", (int) need));
3308     if (need) {
3309 	want = (need + 1) * 2;
3310 
3311 	beginDisplay();
3312 	if (have >= want) {
3313 	    /* EMPTY */ ;
3314 	} else if (have) {
3315 	    have = want;
3316 	    safe_typereallocn(int, majormodes_order, (size_t) have);
3317 	} else {
3318 	    have = want;
3319 	    majormodes_order = typecallocn(int, (size_t) have);
3320 	}
3321 	endofDisplay();
3322 
3323 	if (majormodes_order != 0) {
3324 	    /* set the default order */
3325 	    for (j = 0; j < need; j++) {
3326 		majormodes_order[j] = (int) j;
3327 	    }
3328 	    majormodes_order[need] = -1;
3329 
3330 	    /* handle special cases */
3331 	    for (j = 0; j < need; j++) {
3332 		if ((s = GetMmString(j, MVAL_BEFORE)) != 0) {
3333 		    jj = put_majormode_before(j, s);
3334 		    TRACE(("JUMP %d to %d\n", j, jj));
3335 		    if (jj < (int) j)
3336 			jj = avoid_loop_before(jj, (int) j);
3337 		    if (jj < (int) j)
3338 			j = (UINT) jj;
3339 		}
3340 	    }
3341 	    for (j = 0; j < need; j++) {
3342 		if ((s = GetMmString(j, MVAL_AFTER)) != 0) {
3343 		    jj = put_majormode_after(j, s);
3344 		    TRACE(("JUMP %d to %d\n", j, jj));
3345 		    if (jj < (int) j)
3346 			j = (UINT) jj;
3347 		}
3348 	    }
3349 	    show_majormode_order("final:");
3350 	    check_majormode_order();
3351 	}
3352     }
3353     returnVoid();
3354 }
3355 
3356 /*
3357  * Search my_mode_list[] for the given name, using 'count' for the array size.
3358  * We don't use bsearch because we need to handle insertions into the list.
3359  * If we find it, and will_insert is true, return -1, otherwise the index.
3360  * If we do not find it, return the closest index if will_insert is true,
3361  * otherwise -1.
3362  */
3363 static int
search_mode_list(const char * name,int count,int will_insert)3364 search_mode_list(const char *name, int count, int will_insert)
3365 {
3366     int prev = -1;
3367     int next = 0;
3368     int lo = 0;
3369     int hi = count;
3370     int cmp;
3371     const char *test;
3372 
3373     if (my_mode_list != 0) {
3374 	while (lo < hi) {
3375 	    next = (lo + hi + 0) / 2;
3376 	    if (next == prev) {	/* didn't find it - stop iterating */
3377 		if (will_insert) {
3378 		    next = (strcmp(name, my_mode_list[lo]) > 0
3379 			    ? hi : lo);
3380 		} else {
3381 		    next = -1;
3382 		}
3383 		break;
3384 	    }
3385 	    prev = next;
3386 	    test = ((next < count)
3387 		    ? my_mode_list[next]
3388 		    : (strcmp("\177", "\377") < 0
3389 		       ? "\377"
3390 		       : "\177"));
3391 	    if ((cmp = strcmp(name, test)) == 0) {
3392 		if (will_insert)
3393 		    next = -1;
3394 		break;
3395 	    } else if (cmp > 0) {
3396 		lo = next;
3397 	    } else {
3398 		hi = next;
3399 	    }
3400 	}
3401     } else {
3402 	next = -1;
3403     }
3404     return next;
3405 }
3406 
3407 static int
found_per_submode(const char * majr,int code)3408 found_per_submode(const char *majr, int code)
3409 {
3410     char temp[NSTRING];
3411 
3412     init_my_mode_list();
3413 
3414     (void) per_submode(temp, majr, code, TRUE);
3415     return search_mode_list(temp, (int) count_modes(), FALSE) >= 0;
3416 }
3417 
3418 /*
3419  * Insert 'name' into 'my_mode_list[]', which has 'count' entries.
3420  */
3421 static size_t
insert_per_major(size_t count,const char * name)3422 insert_per_major(size_t count, const char *name)
3423 {
3424     if (name != 0 && *name != EOS && my_mode_list != 0) {
3425 	size_t j, k;
3426 	int found;
3427 
3428 	TRACE(("insert_per_major %lu %s\n", (ULONG) count, name));
3429 	if ((found = search_mode_list(name, (int) count, TRUE)) >= 0) {
3430 	    char *newname = strmalloc(name);
3431 
3432 	    if (newname != 0) {
3433 		j = (size_t) found;
3434 		for (k = ++count; k != j; k--)
3435 		    my_mode_list[k] = my_mode_list[k - 1];
3436 		my_mode_list[j] = newname;
3437 		blist_my_mode_list.itemCount++;
3438 	    } else {
3439 		no_memory("insert_per_major");
3440 	    }
3441 	}
3442     }
3443     return count;
3444 }
3445 
3446 /*
3447  * Remove 'name' from 'my_mode_list[]', which has 'count' entries.
3448  */
3449 static size_t
remove_per_major(size_t count,const char * name)3450 remove_per_major(size_t count, const char *name)
3451 {
3452     if (name != 0 && my_mode_list != 0) {
3453 	int redo = FALSE;
3454 	int found;
3455 	size_t j, k;
3456 
3457 	if ((found = search_mode_list(name, (int) count, FALSE)) >= 0) {
3458 	    j = (size_t) found;
3459 	    if (my_mode_list != all_modes
3460 		&& !in_all_modes(my_mode_list[j])) {
3461 		beginDisplay();
3462 		redo = is_varmode(my_mode_list[j]);
3463 		free(TYPECAST(char, my_mode_list[j]));
3464 		endofDisplay();
3465 	    }
3466 	    count--;
3467 	    for (k = j; k <= count; k++)
3468 		my_mode_list[k] = my_mode_list[k + 1];
3469 	    blist_my_mode_list.itemCount--;
3470 
3471 	    if (redo)
3472 		fill_my_varmodes();
3473 	}
3474     }
3475     return count;
3476 }
3477 
3478 /*
3479  * Lookup a majormode's data area, given its short name, e.g., "c" vs "cmode".
3480  * We store the majormodes in an array to simplify name completion, though this
3481  * complicates definition and removal.
3482  */
3483 static MAJORMODE *
lookup_mm_data(const char * name)3484 lookup_mm_data(const char *name)
3485 {
3486     MAJORMODE *rc = 0;
3487     MAJORMODE_LIST *p = lookup_mm_list(name);
3488 
3489     if (p != 0)
3490 	rc = p->data;
3491 
3492     return rc;
3493 }
3494 
3495 /*
3496  * Lookup a majormode's data area, given its short name, e.g., "c" vs "cmode".
3497  * We store the majormodes in an array to simplify name completion, though this
3498  * complicates definition and removal.
3499  */
3500 static MAJORMODE_LIST *
lookup_mm_list(const char * name)3501 lookup_mm_list(const char *name)
3502 {
3503     MAJORMODE_LIST *rc = 0;
3504 
3505     if (my_majormodes != 0) {
3506 	int n = blist_match(&majormode_blist, name);
3507 	if (n >= 0)
3508 	    rc = my_majormodes + n;
3509     }
3510     return rc;
3511 }
3512 
3513 /* Check if a majormode is predefined.  There are some things we don't want to
3514  * do to them (such as remove them).
3515  */
3516 static int
predef_majormode(const char * name)3517 predef_majormode(const char *name)
3518 {
3519     int rc = FALSE;
3520     MAJORMODE_LIST *p = lookup_mm_list(name);
3521 
3522     if (p != 0)
3523 	rc = p->init;
3524 
3525     return rc;
3526 }
3527 
3528 static int
check_majormode_name(const char * name,int defining)3529 check_majormode_name(const char *name, int defining)
3530 {
3531     int status = TRUE;
3532     if (defining && lookup_mm_data(name) != 0) {
3533 	TRACE(("Mode '%s' already exists\n", name));
3534 	status = SORTOFTRUE;
3535     } else if (!defining && lookup_mm_data(name) == 0) {
3536 	TRACE(("Mode '%s' does not exist\n", name));
3537 	status = SORTOFTRUE;
3538     }
3539     return status;
3540 }
3541 
3542 /* name-completion for "longname" */
3543 int
major_complete(DONE_ARGS)3544 major_complete(DONE_ARGS)
3545 {
3546     return kbd_complete(PASS_DONE_ARGS,
3547 			(const char *) &my_majormodes[0].longname,
3548 			sizeof(my_majormodes[0]));
3549 }
3550 
3551 /* name-completion for "shortname" */
3552 static int
short_major_complete(DONE_ARGS)3553 short_major_complete(DONE_ARGS)
3554 {
3555     return kbd_complete(PASS_DONE_ARGS,
3556 			(const char *) &my_majormodes[0].shortname,
3557 			sizeof(my_majormodes[0]));
3558 }
3559 
3560 static int
prompt_majormode(char ** result,int defining)3561 prompt_majormode(char **result, int defining)
3562 {
3563     static TBUFF *cbuf;		/* buffer to receive mode name into */
3564     char *s;
3565     char *value;
3566     int status;
3567 
3568     /* prompt the user and get an answer */
3569     tb_scopy(&cbuf, "");
3570     if ((status = kbd_reply("majormode: ",
3571 			    &cbuf,
3572 			    eol_history, ' ',
3573 			    KBD_NORMAL,		/* FIXME: KBD_MAYBEC if !defining */
3574 			    (defining || clexec)
3575 			    ? no_completion
3576 			    : short_major_complete)) == TRUE) {
3577 	value = tb_values(cbuf);
3578 	/* check for legal name (alphanumeric) */
3579 	if ((status = is_identifier(value)) != TRUE) {
3580 	    mlwarn("[Not an identifier: %s]", value);
3581 	    return status;
3582 	}
3583 	if ((s = strstr(value, MAJOR_SUFFIX)) != 0
3584 	    && !strcmp(s, MAJOR_SUFFIX)) {
3585 	    if (s == value) {
3586 		mlwarn("[Not a legal majormode identifier: %s]", value);
3587 		return ABORT;
3588 	    }
3589 	    *s = 0;
3590 	}
3591 	if ((status = is_varmode(value)) == TRUE) {
3592 	    *result = value;
3593 	    status = check_majormode_name(*result, defining);
3594 	    if (status != TRUE && status != SORTOFTRUE)
3595 		mlwarn("[No such majormode: %s]", *result);
3596 	    return status;
3597 	}
3598     }
3599     if (status != FALSE)
3600 	mlwarn("[Illegal name: %s]", tb_values(cbuf));
3601     return status;
3602 }
3603 
3604 static int
submode_complete(DONE_ARGS)3605 submode_complete(DONE_ARGS)
3606 {
3607     return kbd_complete(PASS_DONE_ARGS, (const char *) &all_submodes[0],
3608 			sizeof(all_submodes[0]));
3609 }
3610 
3611 /*
3612  * Set the submode values to a known state
3613  */
3614 static void
init_sm_vals(struct VAL * dst)3615 init_sm_vals(struct VAL *dst)
3616 {
3617     int k;
3618     for (k = 0; k < NUM_B_VALUES; k++) {
3619 	make_global_val(dst, global_b_values.bv, k);
3620     }
3621 }
3622 
3623 /*
3624  * Free data allocated in get_sm_vals().
3625  */
3626 static void
free_sm_vals(MAJORMODE * ptr)3627 free_sm_vals(MAJORMODE * ptr)
3628 {
3629     MINORMODE *p;
3630 
3631     while (ptr->sm != 0) {
3632 	p = ptr->sm;
3633 	ptr->sm = p->sm_next;
3634 	free_local_vals(b_valnames, global_b_values.bv, p->sm_vals.bv);
3635 
3636 	beginDisplay();
3637 	free(p->sm_name);
3638 	free(p);
3639 	endofDisplay();
3640     }
3641 }
3642 
3643 /*
3644  * Help initialization of submode groups by copying mode values from the
3645  * base, e.g., ignorecase for majormodes that need this in their fence patterns.
3646  *
3647  * We do not copy the regular expressions since those are what submode groups
3648  * are designed for, and do not want to have leftover patterns from the
3649  * base contaminating the fence-fi, fence-else, etc.
3650  */
3651 static void
copy_sm_vals(struct VAL * dst,struct VAL * src,int type)3652 copy_sm_vals(struct VAL *dst, struct VAL *src, int type)
3653 {
3654     int n;
3655 
3656     if (src != 0) {
3657 	int last = NUM_B_VALUES;
3658 	for (n = 0; n < last; n++) {
3659 	    if (b_valnames[n].type == type
3660 		&& is_local_val(src, n)) {
3661 		dst[n] = src[n];
3662 		make_local_val(dst, n);
3663 	    }
3664 	}
3665     }
3666 }
3667 
3668 /*
3669  * Get/set the 'group' qualifier, which is stored as a property in the
3670  * MAJORMODE structure.  It is used to distinguish MINORMODE structures linked
3671  * to the MAJORMODE.
3672  */
3673 static char *
get_sm_group(MAJORMODE * ptr)3674 get_sm_group(MAJORMODE * ptr)
3675 {
3676     return ptr->mq.qv[QVAL_GROUP].vp->p;
3677 }
3678 
3679 static void
set_sm_group(MAJORMODE * ptr,const char * name)3680 set_sm_group(MAJORMODE * ptr, const char *name)
3681 {
3682     FreeIfNeeded(ptr->mq.qv[QVAL_GROUP].vp->p);
3683     ptr->mq.qv[QVAL_GROUP].vp->p = strmalloc(name);
3684 }
3685 
3686 /*
3687  * Using the currently specified 'group' qualifier, lookup the corresponding
3688  * MINORMODE structure and return a pointer to the B_VALUES VALS data.  If
3689  * no structure is found, create one.
3690  *
3691  * We maintain the list of MINORMODE structures in the order they are defined.
3692  * This has the desirable side-effect of returning immediately for the default
3693  * "" group.
3694  */
3695 static struct VAL *
get_sm_vals(MAJORMODE * ptr)3696 get_sm_vals(MAJORMODE * ptr)
3697 {
3698     struct VAL *result = 0;
3699     if (ptr != 0) {
3700 	MINORMODE *p, *q;
3701 	char *name = get_sm_group(ptr);
3702 
3703 	for (p = ptr->sm, q = 0; p != 0; q = p, p = p->sm_next) {
3704 	    if (!strcmp(name, p->sm_name)) {
3705 		break;
3706 	    }
3707 	}
3708 
3709 	if (p == 0) {
3710 
3711 	    beginDisplay();
3712 	    if ((p = typecalloc(MINORMODE)) != 0) {
3713 		if ((p->sm_name = strmalloc(name)) == 0) {
3714 		    free(p);
3715 		    p = 0;
3716 		}
3717 	    }
3718 	    endofDisplay();
3719 
3720 	    if (p != 0) {
3721 		init_sm_vals(&(p->sm_vals.bv[0]));
3722 		if (ptr->sm != 0) {
3723 		    copy_sm_vals(&(p->sm_vals.bv[0]),
3724 				 &(ptr->sm->sm_vals.bv[0]),
3725 				 VALTYPE_BOOL);
3726 		    copy_sm_vals(&(p->sm_vals.bv[0]),
3727 				 &(ptr->sm->sm_vals.bv[0]),
3728 				 VALTYPE_INT);
3729 		    copy_sm_vals(&(p->sm_vals.bv[0]),
3730 				 &(ptr->sm->sm_vals.bv[0]),
3731 				 VALTYPE_ENUM);
3732 		}
3733 		if (q != 0)
3734 		    q->sm_next = p;
3735 		else
3736 		    ptr->sm = p;
3737 	    }
3738 	}
3739 	if (p != 0) {
3740 	    TRACE2(("...get_sm_vals(%s:%s)\n", ptr->shortname, p->sm_name));
3741 	    result = &(p->sm_vals.bv[0]);
3742 	}
3743     }
3744     return result;
3745 }
3746 
3747 /*
3748  * Returns the name of the n'th submode group
3749  */
3750 const char *
get_submode_name(BUFFER * bp,int n)3751 get_submode_name(BUFFER *bp, int n)
3752 {
3753     const char *result = "";
3754     MAJORMODE *data;
3755 
3756     if (n == 0) {
3757 	result = "";
3758     } else if ((data = bp->majr) != 0) {
3759 	MINORMODE *sm = data->sm;
3760 	while (n-- > 0) {
3761 	    if ((sm = sm->sm_next) == 0) {
3762 		break;
3763 	    } else if (n == 0) {
3764 		result = sm->sm_name;
3765 		break;
3766 	    }
3767 	}
3768     }
3769     return (result);
3770 }
3771 
3772 /*
3773  * Fetch a pointer to the n'th group of submode values.  The 0th group consists
3774  * of the normal buffer values.  Other values are defined in the majormode.
3775  *
3776  * FIXME: shouldn't I have a local submode value for the data which is defined
3777  * in a group?
3778  */
3779 struct VAL *
get_submode_vals(BUFFER * bp,int n)3780 get_submode_vals(BUFFER *bp, int n)
3781 {
3782     struct VAL *result = 0;
3783     MAJORMODE *data;
3784 
3785     if (n == 0) {
3786 	result = bp->b_values.bv;
3787     } else if ((data = bp->majr) != 0) {
3788 	MINORMODE *sm = data->sm;
3789 	while (n-- > 0) {
3790 	    if ((sm = sm->sm_next) == 0) {
3791 		break;
3792 	    } else if (n == 0) {
3793 		result = sm->sm_vals.bv;
3794 		break;
3795 	    }
3796 	}
3797     }
3798     return (result);
3799 }
3800 
3801 struct VAL *
get_submode_valx(BUFFER * bp,int n,int * m)3802 get_submode_valx(BUFFER *bp, int n, int *m)
3803 {
3804     struct VAL *result = 0;
3805     int next = *m + 1;
3806 
3807     if (next != n) {
3808 	if ((result = get_submode_vals(bp, next)) == 0) {
3809 	    next = 0;
3810 	    if (next != n) {
3811 		result = get_submode_vals(bp, next);
3812 	    }
3813 	}
3814 
3815     }
3816     *m = next;
3817     return (result);
3818 }
3819 
3820 /*
3821  * Look in the submode's list of qualifiers, e.g., for "group" or "name".
3822  */
3823 static int
ok_subqual(MAJORMODE * ptr,char * name)3824 ok_subqual(MAJORMODE * ptr, char *name)
3825 {
3826     VALARGS args;
3827     int j;
3828 
3829     if ((j = lookup_valnames(name, q_valnames)) >= 0) {
3830 	args.names = q_valnames;
3831 	args.local = ptr->mq.qv;
3832 	args.global = global_m_values.mv;
3833     } else {
3834 	return FALSE;
3835     }
3836 
3837     args.names += j;
3838     args.global +=j;
3839     args.local += j;
3840 
3841     return adjvalueset(name, TRUE, TRUE, FALSE, &args);
3842 }
3843 
3844 static int
prompt_submode(MAJORMODE * ptr,char ** result,int defining)3845 prompt_submode(MAJORMODE * ptr, char **result, int defining)
3846 {
3847     static TBUFF *cbuf;		/* buffer to receive mode name into */
3848     const char *rp;
3849     int status;
3850 
3851     /* prompt the user and get an answer */
3852     tb_scopy(&cbuf, "");
3853     *result = 0;
3854     while ((status = kbd_reply("submode: ",
3855 			       &cbuf,
3856 			       eol_history, '=',
3857 			       KBD_NORMAL,
3858 			       submode_complete)) == TRUE) {
3859 	if (ok_subqual(ptr, tb_values(cbuf)) == TRUE) {
3860 	    continue;
3861 	} else if ((status = ok_submode(tb_values(cbuf))) == TRUE) {
3862 	    *result = tb_values(cbuf);
3863 	    rp = strip_no(*result);
3864 	    status = check_majormode_name(rp, defining);
3865 	}
3866 	break;
3867     }
3868     if (status != TRUE
3869 	&& status != SORTOFTRUE)
3870 	mlwarn("[Illegal submode name: %s]", tb_values(cbuf));
3871     return status;
3872 }
3873 
3874 /*
3875  * Attach a buffer to the given majormode.  Adjust all of the non-local buffer
3876  * modes to point to the majormode's values where those in turn are local.
3877  */
3878 static int
attach_mmode(BUFFER * bp,const char * name)3879 attach_mmode(BUFFER *bp, const char *name)
3880 {
3881     int n;
3882     VALARGS args;
3883 
3884     if (valid_buffer(bp)) {
3885 	if (bp->majr != 0
3886 	    && strcmp(bp->majr->shortname, name) != 0)
3887 	    (void) detach_mmode(bp, bp->majr->shortname);
3888 
3889 	TRACE(("attach_mmode '%s' to '%s'\n", name, bp->b_bname));
3890 	if ((bp->majr = lookup_mm_data(name)) != 0) {
3891 	    struct VAL *mm = get_sm_vals(bp->majr);
3892 
3893 	    /* adjust buffer modes */
3894 	    for (n = 0; n < NUM_B_VALUES; n++) {
3895 		if (!is_local_b_val(bp, n)
3896 		    && is_local_val(mm, n)) {
3897 		    make_global_val(bp->b_values.bv, mm, n);
3898 		    if (b_valnames[n].side_effect != 0) {
3899 			args.names = &(b_valnames[n]);
3900 			args.local = &(bp->b_values.bv[n]);
3901 			args.global = &mm[n];
3902 			b_valnames[n].side_effect(bp,
3903 						  &args,
3904 						  TRUE,
3905 						  FALSE);
3906 		    }
3907 		} else if (n == MDDOS
3908 			   && is_local_val(mm, n)
3909 			   && !b_val(bp, n)) {
3910 		    /*
3911 		     * This is a special case - we need a way to force vile to
3912 		     * go back and strip the ^M's from the end of each line
3913 		     * when reading a .bat file on a Unix system.  Conversely,
3914 		     * we need a way to suppress this feature selectively in
3915 		     * majormodes which normally use cr/lf endings.
3916 		     */
3917 		    if (mm[MDDOS].v.i) {
3918 			set_rs_crlf(0, 1);
3919 		    } else {
3920 			set_rs_lf(0, 1);
3921 		    }
3922 		}
3923 	    }
3924 	    return TRUE;
3925 	}
3926 	return (bp->majr != 0);
3927     }
3928 
3929     return FALSE;
3930 }
3931 
3932 /*
3933  * Detach a buffer from the given majormode.  Modify the buffer's minor modes
3934  * to point to global modes where they've been pointed to the majormode's data.
3935  */
3936 static int
detach_mmode(BUFFER * bp,const char * name)3937 detach_mmode(BUFFER *bp, const char *name)
3938 {
3939     size_t n;
3940     MAJORMODE *mp;
3941 
3942     if (valid_buffer(bp)
3943 	&& (mp = bp->majr) != 0
3944 	&& !strcmp(mp->shortname, name)) {
3945 	TRACE(("detach_mmode '%s', given '%s'\n", name, mp->shortname));
3946 	/* readjust the buffer's modes */
3947 	for (n = 0; n < NUM_B_VALUES; n++) {
3948 	    if (!is_local_b_val(bp, n)
3949 		&& is_local_val(get_sm_vals(mp), n)) {
3950 		make_global_b_val(bp, n);
3951 	    }
3952 	}
3953 	relist_settings();
3954 	bp->majr = 0;
3955 	return TRUE;
3956     }
3957 
3958     return FALSE;
3959 }
3960 
3961 static int
enable_mmode(const char * name,int flag)3962 enable_mmode(const char *name, int flag)
3963 {
3964     MAJORMODE_LIST *ptr = lookup_mm_list(name);
3965     if (ptr != 0
3966 	&& ptr->flag != flag) {
3967 	ptr->flag = flag;
3968 	return TRUE;
3969     }
3970     return FALSE;
3971 }
3972 
3973 static int
free_majormode(const char * name)3974 free_majormode(const char *name)
3975 {
3976     int result = FALSE;
3977     MAJORMODE *ptr = lookup_mm_data(name);
3978     size_t j, k;
3979     int n;
3980     char temp[NSTRING];
3981     BUFFER *bp;
3982 
3983     if (ptr != 0) {
3984 	int init = TRUE;
3985 	for (j = 0; my_majormodes[j].shortname != 0; j++) {
3986 	    if (my_majormodes[j].data == ptr) {
3987 		init = my_majormodes[j].init;
3988 		for_each_buffer(bp) {
3989 		    if (detach_mmode(bp, my_majormodes[j].shortname)) {
3990 			set_winflags(TRUE, WFHARD | WFMODE);
3991 		    }
3992 		}
3993 		free_local_vals(m_valnames, major_g_vals, ptr->mm.mv);
3994 		free_local_vals(b_valnames, global_b_values.bv, get_sm_vals(ptr));
3995 
3996 		beginDisplay();
3997 		for (k = 0; k < MAX_M_VALUES; k++) {
3998 		    free_val(m_valnames + k, my_majormodes[j].data->mm.mv + k);
3999 		    free(TYPECAST(char, my_majormodes[j].qual[k].name));
4000 		    free(TYPECAST(char, my_majormodes[j].qual[k].shortname));
4001 		}
4002 		for (k = 0; k < MAX_Q_VALUES; k++) {
4003 		    free_val(q_valnames + k, my_majormodes[j].data->mq.qv + k);
4004 		    free(TYPECAST(char, my_majormodes[j].subq[k].name));
4005 		    free(TYPECAST(char, my_majormodes[j].subq[k].shortname));
4006 		}
4007 		free_sm_vals(ptr);
4008 		free(ptr->shortname);
4009 		free(ptr->longname);
4010 		free(ptr);
4011 		endofDisplay();
4012 
4013 		do {
4014 		    my_majormodes[j] = my_majormodes[j + 1];
4015 		} while (my_majormodes[j++].shortname != 0);
4016 		break;
4017 	    }
4018 	}
4019 	if (my_mode_list != all_modes && !init) {
4020 	    j = (size_t) count_modes();
4021 	    j = remove_per_major(j, majorname(temp, name, FALSE));
4022 	    j = remove_per_major(j, majorname(temp, name, TRUE));
4023 	    for (n = 0; n < MAX_M_VALUES; n++) {
4024 		j = remove_per_major(j,
4025 				     per_major(temp, name, (size_t) n, TRUE));
4026 		j = remove_per_major(j,
4027 				     per_major(temp, name, (size_t) n, FALSE));
4028 	    }
4029 	}
4030 	if (major_valnames != 0) {
4031 	    for (n = 0; major_valnames[n].name != 0; n++) {
4032 		if (!strcmp(name, major_valnames[n].shortname)) {
4033 		    beginDisplay();
4034 		    free(TYPECAST(char, major_valnames[n].name));
4035 		    free(TYPECAST(char, major_valnames[n].shortname));
4036 		    endofDisplay();
4037 		    while (major_valnames[n].name != 0) {
4038 			major_valnames[n] = major_valnames[n + 1];
4039 			n++;
4040 		    }
4041 		    break;
4042 		}
4043 	    }
4044 	}
4045 	blist_reset(&majormode_blist, my_majormodes);
4046 	compute_majormodes_order();
4047 	result = TRUE;
4048     }
4049     return result;
4050 }
4051 
4052 static void
init_my_mode_list(void)4053 init_my_mode_list(void)
4054 {
4055     if (my_mode_list == 0) {
4056 	my_mode_list = TYPECAST(const char *, all_modes);
4057 	blist_reset(&blist_my_mode_list, my_mode_list);
4058     }
4059 }
4060 
4061 static size_t
extend_mode_list(int increment)4062 extend_mode_list(int increment)
4063 {
4064     size_t j, k;
4065 
4066     beginDisplay();
4067 
4068     j = (size_t) count_modes();
4069     k = (size_t) increment + j + 1;
4070 
4071     TRACE(("extend_mode_list from %d by %d\n", (int) j, increment));
4072 
4073     if (my_mode_list == all_modes) {
4074 	my_mode_list = typeallocn(const char *, k);
4075 	if (my_mode_list != 0) {
4076 	    memcpy(TYPECAST(char *, my_mode_list), all_modes, (j + 1) *
4077 		   sizeof(*my_mode_list));
4078 	}
4079     } else {
4080 	char **tmp_list = TYPECAST(char *, my_mode_list);
4081 	safe_typereallocn(char *, tmp_list, k);
4082 	my_mode_list = const_varmodes(tmp_list);
4083     }
4084     blist_my_mode_list.theList = my_mode_list;
4085     check_my_varmodes(k);
4086     endofDisplay();
4087 
4088     return j;
4089 }
4090 
4091 static struct VAL *
extend_VAL_array(struct VAL * ptr,size_t item,size_t len)4092 extend_VAL_array(struct VAL *ptr, size_t item, size_t len)
4093 {
4094     size_t j, k;
4095 
4096     TRACE(("extend_VAL_array %p item %ld of %ld\n",
4097 	   (void *) ptr, (long) item, (long) len));
4098 
4099     if (ptr == 0) {
4100 	beginDisplay();
4101 	ptr = typeallocn(struct VAL, len + 1);
4102 	endofDisplay();
4103     } else {
4104 	beginDisplay();
4105 	safe_typereallocn(struct VAL, ptr, len + 1);
4106 	endofDisplay();
4107 
4108 	if (ptr != 0) {
4109 	    for (j = 0; j < len; j++) {
4110 		k = (j >= item) ? j + 1 : j;
4111 		ptr[k] = ptr[j];
4112 		make_local_val(ptr, k);
4113 	    }
4114 	}
4115     }
4116     if (ptr != 0) {
4117 	make_local_val(ptr, item);
4118 	make_local_val(ptr, len);
4119 	ptr[item].v.i = FALSE;
4120     }
4121     return ptr;
4122 }
4123 
4124 static void
set_qualifier(const struct VALNAMES * names,struct VAL * values,const char * s,int n)4125 set_qualifier(const struct VALNAMES *names,
4126 	      struct VAL *values,
4127 	      const char *s,
4128 	      int n)
4129 {
4130     free_val(names, values);
4131     switch (names->type) {
4132     case VALTYPE_BOOL:
4133     case VALTYPE_ENUM:
4134     case VALTYPE_INT:
4135 	values->v.i = n;
4136 	break;
4137     case VALTYPE_STRING:
4138 	values->v.p = strmalloc(s);
4139 	break;
4140     case VALTYPE_REGEX:
4141 	values->v.r = new_regexval(s, TRUE);
4142 	break;
4143     }
4144     make_local_val(values, 0);
4145 }
4146 
4147 static void
reset_qualifier(const struct VALNAMES * names,struct VAL * values)4148 reset_qualifier(const struct VALNAMES *names, struct VAL *values)
4149 {
4150     set_qualifier(names, values, "", 0);
4151 }
4152 
4153 /*
4154  * Reset/initialize all of the qualifiers.  We will use any that become set as
4155  * parameters for the current submode operation.
4156  */
4157 static void
reset_sm_qualifiers(MAJORMODE * ptr)4158 reset_sm_qualifiers(MAJORMODE * ptr)
4159 {
4160     int k;
4161     for (k = 0; k < MAX_Q_VALUES; k++)
4162 	reset_qualifier(q_valnames + k, ptr->mq.qv + k);
4163 }
4164 
4165 /*
4166  * Buffer-animation for [Major Modes]
4167  */
4168 #if OPT_UPBUFF
4169 static int
show_majormodes(BUFFER * bp)4170 show_majormodes(BUFFER *bp)
4171 {
4172     b_clr_obsolete(bp);
4173     return list_majormodes(FALSE, 1);
4174 }
4175 
4176 static void
relist_majormodes(void)4177 relist_majormodes(void)
4178 {
4179     update_scratch(MAJORMODES_BufName, show_majormodes);
4180 }
4181 #endif /* OPT_UPBUFF */
4182 
4183 static int
show_active_majormodes(int nflag)4184 show_active_majormodes(int nflag)
4185 {
4186     int n;
4187     for (n = 0; major_valnames[n].name != 0; n++) {
4188 	make_local_val(major_g_vals, n);
4189 	major_g_vals[n].vp->i = my_majormodes[n].flag;
4190     }
4191     return listvalueset("Majormodes", nflag, FALSE, major_valnames,
4192 			major_g_vals, (struct VAL *) 0);
4193 }
4194 
4195 /* list the current modes into the current buffer */
4196 /* ARGSUSED */
4197 static void
makemajorlist(int local,void * ptr GCC_UNUSED)4198 makemajorlist(int local, void *ptr GCC_UNUSED)
4199 {
4200     int j;
4201     int nflag;
4202     MAJORMODE *data;
4203     MINORMODE *vals;
4204 
4205     if (my_majormodes != 0) {
4206 	unsigned count = (unsigned) count_majormodes();
4207 	if (show_active_majormodes(0))
4208 	    bputc('\n');
4209 	for (j = 0; my_majormodes[j].shortname != 0; j++) {
4210 	    if ((data = my_majormodes[j].data) == 0) {
4211 		continue;
4212 	    } else if (j != 0) {
4213 		bputc('\n');
4214 	    }
4215 
4216 	    if (local)
4217 		TheMajor = my_majormodes[j].shortname;
4218 
4219 	    bprintf("--- \"%s\" majormode settings ",
4220 		    my_majormodes[j].shortname);
4221 	    bpadc('-', term.cols - 12 - DOT.o);
4222 	    bprintf("(%d:%u)",
4223 		    find_majormode_order(j) + 1,
4224 		    count);
4225 	    bpadc('-', term.cols - DOT.o);
4226 	    bputc('\n');
4227 
4228 	    nflag = listvalueset("Qualifier", FALSE, TRUE,
4229 				 m_valnames,
4230 				 data->mm.mv,
4231 				 data->mm.mv);
4232 	    for (vals = data->sm; vals != 0; vals = vals->sm_next) {
4233 		char *group = vals->sm_name;
4234 		char *name;
4235 
4236 		beginDisplay();
4237 		name = typeallocn(char, 80 + strlen(group));
4238 		endofDisplay();
4239 
4240 		if (name != 0) {
4241 		    if (*group)
4242 			lsprintf(name, "Buffer (\"%s\" group)", group);
4243 		    else
4244 			strcpy(name, "Buffer");
4245 		    nflag = listvalueset(name, nflag, TRUE,
4246 					 b_valnames,
4247 					 vals->sm_vals.bv,
4248 					 global_b_values.bv);
4249 		    beginDisplay();
4250 		    free(name);
4251 		    endofDisplay();
4252 		}
4253 	    }
4254 	}
4255     }
4256     TheMajor = 0;
4257 }
4258 
4259 /* ARGSUSED */
4260 int
list_majormodes(int f,int n GCC_UNUSED)4261 list_majormodes(int f, int n GCC_UNUSED)
4262 {
4263     WINDOW *wp = curwp;
4264     int s;
4265 
4266     TRACE((T_CALLED "list_majormodes(f=%d)\n", f));
4267 
4268     s = liststuff(MAJORMODES_BufName, FALSE, makemajorlist, f, (void *) wp);
4269     /* back to the buffer whose modes we just listed */
4270     if (swbuffer(wp->w_bufp))
4271 	curwp = wp;
4272 
4273     returnCode(s);
4274 }
4275 
4276 int
alloc_mode(const char * shortname,int predef)4277 alloc_mode(const char *shortname, int predef)
4278 {
4279     size_t j = 0, k;
4280     int n;
4281     int status = TRUE;
4282     char longname[NSTRING];
4283     char temp[NSTRING];
4284     static struct VAL *new_vals;
4285 
4286     TRACE((T_CALLED "alloc_mode(%s,%d)\n", shortname, predef));
4287     if (major_valnames == 0) {
4288 	beginDisplay();
4289 	major_valnames = typecallocn(struct VALNAMES, (size_t) 2);
4290 	endofDisplay();
4291 	j = 0;
4292 	k = 1;
4293     } else {
4294 	k = count_majormodes();
4295 	beginDisplay();
4296 	safe_typereallocn(struct VALNAMES, major_valnames, k + 2);
4297 	endofDisplay();
4298 	if (major_valnames != 0) {
4299 	    for (j = k++; j != 0; j--) {
4300 		major_valnames[j] = major_valnames[j - 1];
4301 		if (strcmp(major_valnames[j - 1].shortname, shortname) < 0) {
4302 		    break;
4303 		}
4304 	    }
4305 	}
4306     }
4307 
4308     if (major_valnames == 0)
4309 	returnCode(FALSE);
4310 
4311     (void) majorname(longname, shortname, TRUE);
4312     major_valnames[j].name = strmalloc(longname);
4313     major_valnames[j].shortname = strmalloc(shortname);
4314     major_valnames[j].type = VALTYPE_MAJOR;
4315     major_valnames[j].side_effect = chgd_win_mode;
4316 
4317     if (major_valnames[j].name == 0
4318 	|| major_valnames[j].shortname == 0) {
4319 	do {
4320 	    major_valnames[j] = major_valnames[j + 1];
4321 	    ++j;
4322 	} while (major_valnames[j].name != 0);
4323 	returnCode(FALSE);
4324     }
4325 
4326     memset(major_valnames + k, 0, sizeof(*major_valnames));
4327 
4328     /* build arrays needed for 'find_mode()' bookkeeping */
4329     if ((new_vals = extend_VAL_array(major_g_vals, j, k)) == 0)
4330 	returnCode(FALSE);
4331     major_g_vals = new_vals;
4332 
4333     if ((new_vals = extend_VAL_array(major_l_vals, j, k)) == 0)
4334 	returnCode(FALSE);
4335     major_l_vals = new_vals;
4336 
4337     if (my_majormodes == 0) {
4338 	beginDisplay();
4339 	my_majormodes = typecallocn(MAJORMODE_LIST, (size_t) 2);
4340 	endofDisplay();
4341 	j = 0;
4342 	k = 1;
4343     } else {
4344 	k = count_majormodes();
4345 	beginDisplay();
4346 	safe_typereallocn(MAJORMODE_LIST, my_majormodes, k + 2);
4347 	endofDisplay();
4348 	if (my_majormodes) {
4349 	    for (j = k++; j != 0; j--) {
4350 		my_majormodes[j] = my_majormodes[j - 1];
4351 		if (strcmp(my_majormodes[j - 1].shortname, shortname) < 0) {
4352 		    break;
4353 		}
4354 	    }
4355 	}
4356     }
4357     blist_reset(&majormode_blist, my_majormodes);
4358 
4359     if (my_majormodes == 0)
4360 	returnCode(FALSE);
4361 
4362     /* allocate the names for the major mode */
4363     beginDisplay();
4364 
4365     my_majormodes[j].shortname = strmalloc(shortname);
4366     my_majormodes[j].longname = strmalloc(longname);
4367 
4368     my_majormodes[j].init = predef;
4369     my_majormodes[j].flag = TRUE;
4370 
4371     if ((my_majormodes[j].data = typecalloc(MAJORMODE)) != 0) {
4372 	my_majormodes[j].data->shortname = my_majormodes[j].shortname;
4373 	my_majormodes[j].data->longname = my_majormodes[j].longname;
4374 
4375 	memset(my_majormodes + k, 0, sizeof(*my_majormodes));
4376     }
4377 
4378     endofDisplay();
4379 
4380     if (my_majormodes[j].shortname == 0
4381 	|| my_majormodes[j].longname == 0
4382 	|| my_majormodes[j].data == 0) {
4383 	beginDisplay();
4384 	FreeIfNeeded(my_majormodes[j].data);
4385 	FreeIfNeeded(my_majormodes[j].longname);
4386 	endofDisplay();
4387 	do {
4388 	    my_majormodes[j] = my_majormodes[j + 1];
4389 	    ++j;
4390 	} while (my_majormodes[j].shortname != 0);
4391 	returnCode(FALSE);
4392     }
4393 
4394     /* copy array to get types, then overwrite the name-pointers */
4395     memcpy(my_majormodes[j].subq, q_valnames, sizeof(q_valnames));
4396     for (k = 0; k < MAX_Q_VALUES; k++) {
4397 	reset_qualifier(q_valnames + k, my_majormodes[j].data->mq.qv + k);
4398 
4399 	my_majormodes[j].subq[k].name =
4400 	    strmalloc(q_valnames[k].name);
4401 	my_majormodes[j].subq[k].shortname =
4402 	    strmalloc(q_valnames[k].shortname);
4403 
4404 	if (my_majormodes[j].subq[k].name == 0
4405 	    || my_majormodes[j].subq[k].shortname == 0) {
4406 	    free_majormode(shortname);
4407 	    returnCode(FALSE);
4408 	}
4409     }
4410 
4411     init_sm_vals(get_sm_vals(my_majormodes[j].data));
4412 
4413     /* copy array to get types, then overwrite the name-pointers */
4414     memcpy(my_majormodes[j].qual, m_valnames, sizeof(m_valnames));
4415     for (k = 0; k < MAX_M_VALUES; k++) {
4416 	reset_qualifier(m_valnames + k, my_majormodes[j].data->mm.mv + k);
4417 
4418 	my_majormodes[j].qual[k].name =
4419 	    strmalloc(per_major(temp, shortname, k, TRUE));
4420 	my_majormodes[j].qual[k].shortname =
4421 	    strmalloc(per_major(temp, shortname, k, FALSE));
4422 
4423 	if (my_majormodes[j].qual[k].name == 0
4424 	    || my_majormodes[j].qual[k].shortname == 0) {
4425 	    free_majormode(shortname);
4426 	    returnCode(FALSE);
4427 	}
4428     }
4429 
4430     /*
4431      * Create the majormode-specific names.  If this is predefined, we
4432      * already had mktbls do this.
4433      */
4434     if (!predef) {
4435 	j = extend_mode_list((MAX_M_VALUES + 2) * 2);
4436 	j = insert_per_major(j, majorname(longname, shortname, FALSE));
4437 	j = insert_per_major(j, majorname(longname, shortname, TRUE));
4438 	for (n = 0; n < MAX_M_VALUES; n++) {
4439 	    j = insert_per_major(j, per_major(temp, shortname, (size_t) n, TRUE));
4440 	    j = insert_per_major(j, per_major(temp, shortname, (size_t) n, FALSE));
4441 	}
4442     }
4443 
4444     compute_majormodes_order();
4445     returnCode(status);
4446 }
4447 
4448 /* ARGSUSED */
4449 int
define_mode(int f GCC_UNUSED,int n GCC_UNUSED)4450 define_mode(int f GCC_UNUSED, int n GCC_UNUSED)
4451 {
4452     char *name;
4453     int status;
4454 
4455     if ((status = prompt_majormode(&name, TRUE)) == TRUE) {
4456 	TRACE(("define majormode:%s\n", name));
4457 	status = alloc_mode(name, FALSE);
4458 	relist_settings();
4459 	relist_majormodes();
4460     } else if (status == SORTOFTRUE) {
4461 	status = TRUE;		/* don't complain if it's already true */
4462     }
4463     return status;
4464 }
4465 
4466 static void
copy_derived_submodes(const char * newname,const char * oldname)4467 copy_derived_submodes(const char *newname, const char *oldname)
4468 {
4469     MAJORMODE_LIST *old_mm;
4470     MAJORMODE_LIST *new_mm;
4471     MINORMODE *old_sm;
4472 
4473     if ((old_mm = lookup_mm_list(oldname)) != 0 &&
4474 	(new_mm = lookup_mm_list(newname)) != 0) {
4475 	MAJORMODE *source = old_mm->data;
4476 	MAJORMODE *target = new_mm->data;
4477 
4478 	clone_mvals(NUM_M_VALUES,
4479 		    target->mm.mv,
4480 		    source->mm.mv,
4481 		    m_valnames);
4482 	for (old_sm = source->sm; old_sm != 0; old_sm = old_sm->sm_next) {
4483 	    set_sm_group(target, old_sm->sm_name);
4484 	    clone_mvals(NUM_B_VALUES,
4485 			get_sm_vals(target),
4486 			old_sm->sm_vals.bv,
4487 			b_valnames);
4488 	}
4489     }
4490 }
4491 
4492 /* ARGSUSED */
4493 int
derive_mode(int f GCC_UNUSED,int n GCC_UNUSED)4494 derive_mode(int f GCC_UNUSED, int n GCC_UNUSED)
4495 {
4496     char *oldname;
4497     char *newname;
4498     TBUFF *newtb = 0;
4499     int status;
4500 
4501     if ((status = prompt_majormode(&newname, TRUE)) == TRUE
4502 	&& tb_scopy(&newtb, newname) != 0
4503 	&& (status = prompt_majormode(&oldname, FALSE)) == TRUE) {
4504 	TRACE(("derive majormode %s->%s\n", oldname, newname));
4505 	status = alloc_mode(tb_values(newtb), FALSE);
4506 	copy_derived_submodes(tb_values(newtb), oldname);
4507 	relist_settings();
4508 	relist_majormodes();
4509     } else if (status == SORTOFTRUE) {
4510 	status = TRUE;		/* don't complain if it's already true */
4511     }
4512     tb_free(&newtb);
4513     return status;
4514 }
4515 
4516 static int
do_a_submode(int defining)4517 do_a_submode(int defining)
4518 {
4519     char *name;
4520     char *subname;
4521     int status;
4522     MAJORMODE *ptr;
4523     VALARGS args;
4524     int j;
4525     size_t k;
4526     int qualifier = FALSE;
4527     char temp[NSTRING];
4528     const char *rp;
4529 
4530     if ((status = prompt_majormode(&name, FALSE)) != TRUE)
4531 	return status;
4532 
4533     if ((ptr = lookup_mm_data(name)) == 0)
4534 	return FALSE;
4535     reset_sm_qualifiers(ptr);
4536 
4537     if ((status = prompt_submode(ptr, &subname, TRUE)) != TRUE) {
4538 	reset_sm_qualifiers(ptr);
4539 	return status;
4540     }
4541 
4542     rp = strip_no(subname);
4543     if ((j = lookup_valnames(rp, m_valnames)) >= 0) {
4544 	qualifier = TRUE;
4545 	args.names = m_valnames;
4546 	args.local = ptr->mm.mv;
4547 	args.global = global_m_values.mv;
4548     } else if ((j = lookup_valnames(rp, b_valnames)) >= 0) {
4549 	args.names = b_valnames;
4550 	args.local = get_sm_vals(ptr);
4551 	args.global = defining ? args.local : global_b_values.bv;
4552     } else {
4553 	mlwarn("[BUG: no such submode: %s]", rp);
4554 	reset_sm_qualifiers(ptr);
4555 	return FALSE;
4556     }
4557 
4558     args.names += j;
4559     args.global +=j;
4560     args.local += j;
4561 
4562     /*
4563      * We store submodes in the majormode as local values.
4564      */
4565     status = adjvalueset(subname, TRUE, defining, FALSE, &args);
4566 
4567     /*
4568      * Check if we deleted one of the qualifiers, since there's no global
4569      * value to inherit back to, we'll have to ensure there's valid data.
4570      */
4571     if (status == TRUE
4572 	&& qualifier
4573 	&& !defining) {
4574 	reset_qualifier(args.names, args.global);
4575     }
4576 
4577     if (status == TRUE) {
4578 	if (qualifier) {
4579 	    switch (j) {
4580 	    case MVAL_AFTER:
4581 	    case MVAL_BEFORE:
4582 	    case MVAL_QUALIFIERS:
4583 		compute_majormodes_order();
4584 		break;
4585 	    default:
4586 		break;
4587 	    }
4588 	} else {
4589 	    if (defining && found_per_submode(name, j)) {
4590 		TRACE(("submode names for %d present\n", j)) /*EMPTY */ ;
4591 	    } else if (defining) {
4592 		TRACE(("construct submode names for %d\n", j));
4593 		k = extend_mode_list(2);
4594 		k = insert_per_major(k,
4595 				     per_submode(temp, name, j, TRUE));
4596 		(void) insert_per_major(k,
4597 					per_submode(temp, name, j, FALSE));
4598 	    } else {
4599 		TRACE(("destroy submode names for %d\n", j));
4600 		k = (size_t) count_modes();
4601 		k = remove_per_major(k,
4602 				     per_submode(temp, name, j, TRUE));
4603 		(void) remove_per_major(k,
4604 					per_submode(temp, name, j, FALSE));
4605 	    }
4606 	}
4607     }
4608 
4609     /* FIXME: remember to adjust all buffers that used this mode, in
4610      * case we make a minor mode part-of or removed from the major mode.
4611      */
4612     relist_settings();
4613     relist_majormodes();
4614     reset_sm_qualifiers(ptr);
4615     return status;
4616 }
4617 
4618 /* ARGSUSED */
4619 int
define_submode(int f GCC_UNUSED,int n GCC_UNUSED)4620 define_submode(int f GCC_UNUSED, int n GCC_UNUSED)
4621 {
4622     return do_a_submode(TRUE);
4623 }
4624 
4625 /* ARGSUSED */
4626 int
remove_submode(int f GCC_UNUSED,int n GCC_UNUSED)4627 remove_submode(int f GCC_UNUSED, int n GCC_UNUSED)
4628 {
4629     return do_a_submode(FALSE);
4630 }
4631 
4632 /* ARGSUSED */
4633 int
remove_mode(int f GCC_UNUSED,int n GCC_UNUSED)4634 remove_mode(int f GCC_UNUSED, int n GCC_UNUSED)
4635 {
4636     char *name;
4637     int status;
4638 
4639     if ((status = prompt_majormode(&name, FALSE)) == TRUE) {
4640 	if ((status = !predef_majormode(name)) == TRUE) {
4641 	    TRACE(("remove majormode:%s\n", name));
4642 	    free_majormode(name);
4643 	    relist_settings();
4644 	    relist_majormodes();
4645 	} else {
4646 	    mlwarn("[This is a predefined mode: %s]", name);
4647 	}
4648     } else if (status == SORTOFTRUE) {
4649 	status = TRUE;
4650     }
4651     return status;
4652 }
4653 
4654 /*
4655  * For the given majormode (by index into my_majormodes[]), return the
4656  * corresponding buffer mode value.
4657  */
4658 static int
get_sm_b_val(int n,int m)4659 get_sm_b_val(int n, int m)
4660 {
4661     struct VAL *bv = get_sm_vals(my_majormodes[n].data);
4662     return (bv[m].vp->i);
4663 }
4664 
4665 /*
4666  * For the given majormode (by index into my_majormodes[]), return the
4667  * corresponding buffer mode value.
4668  */
4669 static regexp *
get_sm_rexp(int n,int m)4670 get_sm_rexp(int n, int m)
4671 {
4672     struct VAL *mv = get_sm_vals(my_majormodes[n].data);
4673 
4674     if (mv[m].vp != 0
4675 	&& mv[m].vp->r != 0
4676 	&& mv[m].vp->r->pat != 0
4677 	&& mv[m].vp->r->pat[0] != 0
4678 	&& mv[m].vp->r->reg != 0) {
4679 	TRACE2(("get_sm_rexp(%s) %s\n",
4680 		my_majormodes[n].shortname,
4681 		mv[m].vp->r->pat));
4682 	return mv[m].vp->r->reg;
4683     }
4684     return 0;
4685 }
4686 
4687 /*
4688  * Use a regular expression (normally a suffix, such as ".c") to match the
4689  * buffer's filename.
4690  */
4691 static int
test_by_suffix(int n,BUFFER * bp)4692 test_by_suffix(int n, BUFFER *bp)
4693 {
4694     int result = -1;
4695 
4696     if (my_majormodes[n].flag) {
4697 	regexp *exp;
4698 	char *pathname = bp->b_fname;
4699 	char *filename;
4700 	char *suffix;
4701 	TBUFF *savename = 0;
4702 #if OPT_VMS_PATH
4703 	TBUFF *stripname = 0;
4704 #endif
4705 
4706 	int ic = global_g_val(GMDFILENAME_IC) || get_sm_b_val(n, MDIGNCASE);
4707 #if OPT_VMS_PATH
4708 	tb_scopy(&stripname, pathname);
4709 	pathname = tb_values(stripname);
4710 	strip_version(pathname);
4711 #endif
4712 
4713 	if (((exp = get_sm_rexp(n, VAL_STRIPSUFFIX)) != 0
4714 	     || (exp = b_val_rexp(bp, VAL_STRIPSUFFIX)->reg) != 0)
4715 	    && nregexec(exp, pathname, (char *) 0, 0, -1, ic)) {
4716 	    if (tb_scopy(&savename, pathname) != 0) {
4717 		strcpy(tb_values(savename) + (exp->startp[0] - pathname),
4718 		       exp->endp[0]);
4719 		pathname = tb_values(savename);
4720 	    }
4721 	}
4722 	filename = pathleaf(pathname);
4723 	suffix = strchr(filename, '.');
4724 #if OPT_VMS_PATH
4725 	if (suffix != 0 && suffix[1] == 0) {
4726 	    *suffix = 0;
4727 	    suffix = 0;
4728 	}
4729 #endif
4730 
4731 	if ((exp = get_mm_rexp(n, MVAL_MODE_PATHNAME)) != 0
4732 	    && nregexec(exp, pathname, (char *) 0, 0, -1, ic)) {
4733 	    TRACE(("test_by_pathname(%s) %s\n",
4734 		   pathname,
4735 		   my_majormodes[n].shortname));
4736 	    result = n;
4737 	} else if ((exp = get_mm_rexp(n, MVAL_MODE_FILENAME)) != 0
4738 		   && nregexec(exp, filename, (char *) 0, 0, -1, ic)) {
4739 	    TRACE(("test_by_filename(%s) %s %s\n",
4740 		   pathname,
4741 		   filename,
4742 		   my_majormodes[n].shortname));
4743 	    result = n;
4744 	} else if (!isShellOrPipe(pathname)
4745 		   && suffix != 0
4746 		   && (exp = get_mm_rexp(n, MVAL_MODE_SUFFIXES)) != 0
4747 		   && nregexec(exp, suffix, (char *) 0, 0, -1, ic)) {
4748 	    TRACE(("test_by_suffixes(%s) %s %s\n",
4749 		   pathname,
4750 		   suffix,
4751 		   my_majormodes[n].shortname));
4752 	    result = n;
4753 	}
4754 	tb_free(&savename);
4755 #if OPT_VMS_PATH
4756 	tb_free(&stripname);
4757 #endif
4758     }
4759     return result;
4760 }
4761 
4762 static LINE *
get_preamble(BUFFER * bp)4763 get_preamble(BUFFER *bp)
4764 {
4765     if (!is_empty_buf(bp)) {
4766 	LINE *lp = lforw(buf_head(bp));
4767 	if (lisreal(lp))
4768 	    return lp;
4769     }
4770     return 0;
4771 }
4772 
4773 /*
4774  * Match the first line of the buffer against a regular expression.
4775  */
4776 static int
test_by_preamble(int n,BUFFER * bp GCC_UNUSED,LINE * lp)4777 test_by_preamble(int n, BUFFER *bp GCC_UNUSED, LINE *lp)
4778 {
4779     int result = -1;
4780 
4781     if (lp != 0
4782 	&& my_majormodes[n].flag) {
4783 	regexp *exp = get_mm_rexp(n, MVAL_PREAMBLE);
4784 	int ic = global_g_val(GMDFILENAME_IC) || get_sm_b_val(n, MDIGNCASE);
4785 	if (exp != 0
4786 	    && lregexec(exp, lp, 0, llength(lp), ic)) {
4787 	    TRACE(("test_by_preamble(%s) %s\n",
4788 		   bp->b_fname,
4789 		   my_majormodes[n].shortname));
4790 	    result = n;
4791 	}
4792     }
4793     return result;
4794 }
4795 
4796 static int
need_suffix_and_preamble(int n)4797 need_suffix_and_preamble(int n)
4798 {
4799     if (get_mm_number(n, MVAL_QUALIFIERS) == MMQ_ALL
4800 	&& (get_mm_rexp(n, MVAL_MODE_PATHNAME) != 0
4801 	    || get_mm_rexp(n, MVAL_MODE_FILENAME) != 0
4802 	    || get_mm_rexp(n, MVAL_MODE_SUFFIXES) != 0)
4803 	&& get_mm_rexp(n, MVAL_PREAMBLE) != 0) {
4804 	TRACE(("need both suffix/preamble for %s\n",
4805 	       my_majormodes[n].shortname));
4806 	return TRUE;
4807     }
4808     return FALSE;
4809 }
4810 
4811 void
infer_majormode(BUFFER * bp)4812 infer_majormode(BUFFER *bp)
4813 {
4814     static int level;
4815 
4816     TRACE((T_CALLED "infer_majormode(%s)\n", bp->b_bname));
4817 
4818     if (level++) {
4819 	;
4820     } else if (my_majormodes != 0
4821 	       && majormodes_order != 0
4822 	       && !b_is_directory(bp)
4823 	       && bp->b_fname != 0
4824 	       && !(is_internalname(bp->b_fname)
4825 		    && !eql_bname(bp, STDIN_BufName)
4826 		    && !eql_bname(bp, OUTPUT_BufName))) {
4827 	int n, m;
4828 	int result = -1;
4829 	LINE *lp = get_preamble(bp);
4830 
4831 	did_attach_mmode = FALSE;
4832 	if (bp == curbp		/* otherwise we cannot script it */
4833 	    && run_a_hook(&majormodehook) == TRUE
4834 	    && did_attach_mmode) {
4835 	    ;
4836 	} else {
4837 	    for (m = 0; (n = majormodes_order[m]) >= 0; m++) {
4838 		if (need_suffix_and_preamble(n)) {
4839 		    if (test_by_suffix(n, bp) >= 0
4840 			&& test_by_preamble(n, bp, lp) >= 0) {
4841 			TPRINTF(("matched preamble and suffix of %s\n", bp->b_bname));
4842 			result = n;
4843 			break;
4844 		    }
4845 		} else if (test_by_suffix(n, bp) >= 0) {
4846 		    TPRINTF(("matched suffix of %s\n", bp->b_bname));
4847 		    result = n;
4848 		    break;
4849 		} else if (test_by_preamble(n, bp, lp) >= 0) {
4850 		    result = n;
4851 		    TPRINTF(("matched preamble of %s\n", bp->b_bname));
4852 		    break;
4853 		}
4854 	    }
4855 	    if (result >= 0) {
4856 		TPRINTF(("...inferred majormode %s\n", my_majormodes[result].shortname));
4857 		attach_mmode(bp, my_majormodes[result].shortname);
4858 	    }
4859 	}
4860     }
4861     --level;
4862 
4863     returnVoid();
4864 }
4865 
4866 void
set_submode_val(const char * name,int n,int value)4867 set_submode_val(const char *name, int n, int value)
4868 {
4869     MAJORMODE *p;
4870 
4871     TRACE((T_CALLED "set_submode_val(%s, %d, %d)\n", name, n, value));
4872     if ((p = lookup_mm_data(name)) != 0) {
4873 	struct VAL *q = get_sm_vals(p);
4874 	q[n].v.i = value;
4875 	make_local_val(q, n);
4876     }
4877     returnVoid();
4878 }
4879 
4880 void
set_submode_txt(const char * name,int n,const char * value)4881 set_submode_txt(const char *name, int n, const char *value)
4882 {
4883     MAJORMODE *p;
4884 
4885     TRACE((T_CALLED "set_submode_txt(%s, %d, %s)\n", name, n, value));
4886     if ((p = lookup_mm_data(name)) != 0) {
4887 	struct VAL *q = get_sm_vals(p);
4888 	if (q != 0) {
4889 	    q[n].v.p = strmalloc(value);
4890 	    make_local_val(q, n);
4891 	}
4892     }
4893     returnVoid();
4894 }
4895 
4896 void
set_majormode_rexp(const char * name,int n,const char * r)4897 set_majormode_rexp(const char *name, int n, const char *r)
4898 {
4899     MAJORMODE *p;
4900 
4901     TRACE((T_CALLED "set_majormode_rexp(%s, %d, %s)\n", name, n, r));
4902     if ((p = lookup_mm_data(name)) != 0)
4903 	set_qualifier(m_valnames + n, p->mm.mv + n, r, 0);
4904     returnVoid();
4905 }
4906 
4907 /*ARGSUSED*/
4908 int
chgd_mm_order(BUFFER * bp GCC_UNUSED,VALARGS * args GCC_UNUSED,int glob_vals GCC_UNUSED,int testing GCC_UNUSED)4909 chgd_mm_order(BUFFER *bp GCC_UNUSED,
4910 	      VALARGS * args GCC_UNUSED,
4911 	      int glob_vals GCC_UNUSED,
4912 	      int testing GCC_UNUSED)
4913 {
4914     if (!testing) {
4915 	compute_majormodes_order();
4916 	relist_majormodes();
4917     }
4918     return TRUE;
4919 }
4920 
4921 /*ARGSUSED*/
4922 int
chgd_filter(BUFFER * bp,VALARGS * args,int glob_vals,int testing)4923 chgd_filter(BUFFER *bp, VALARGS * args, int glob_vals, int testing)
4924 {
4925     if (!testing) {
4926 	struct VAL *values = args->local;
4927 	BUFFER *bp2;
4928 
4929 	if (values->vp->i == FALSE) {
4930 	    if (glob_vals && bp->majr) {
4931 		/*
4932 		 * Check whether we are being called to suppress highlighting
4933 		 * globally, or as part of a submode setting.  In the latter
4934 		 * case, we will discard attributes only for buffers using
4935 		 * the majormode which is being updated.
4936 		 */
4937 		struct VAL *check = get_sm_vals(bp->majr);
4938 		int submode = (args->global -check) == MDHILITE;
4939 
4940 		for_each_buffer(bp2) {
4941 		    if (!submode || (check == get_sm_vals(bp2->majr))) {
4942 			free_attribs(bp2);
4943 		    }
4944 		}
4945 	    } else {
4946 		free_attribs(bp);
4947 	    }
4948 	}
4949 	set_winflags(glob_vals, WFHARD);
4950     }
4951     return TRUE;
4952 }
4953 
4954 /*ARGSUSED*/
4955 int
reset_majormode(int f GCC_UNUSED,int n GCC_UNUSED)4956 reset_majormode(int f GCC_UNUSED, int n GCC_UNUSED)
4957 {
4958     if (curbp->majr != 0)
4959 	(void) detach_mmode(curbp, curbp->majr->shortname);
4960     infer_majormode(curbp);
4961     set_winflags(TRUE, WFMODE);
4962     return TRUE;
4963 }
4964 
4965 void
set_tagsmode(BUFFER * bp)4966 set_tagsmode(BUFFER *bp)
4967 {
4968     static const char *my_mode = "tags" MAJOR_SUFFIX;
4969     VALARGS args;
4970 
4971     if (find_mode(bp, my_mode, FALSE, &args) == TRUE) {
4972 	(void) set_mode_value(bp, my_mode, FALSE, TRUE, FALSE, &args,
4973 			      (char *) 0);
4974     }
4975 }
4976 
4977 void
set_vilemode(BUFFER * bp)4978 set_vilemode(BUFFER *bp)
4979 {
4980     static const char *my_mode = "vile" MAJOR_SUFFIX;
4981     VALARGS args;
4982 
4983     if (find_mode(bp, my_mode, FALSE, &args) == TRUE) {
4984 	(void) set_mode_value(bp, my_mode, FALSE, TRUE, FALSE, &args,
4985 			      (char *) 0);
4986     }
4987 }
4988 #endif /* OPT_MAJORMODE */
4989 
4990 /*--------------------------------------------------------------------------*/
4991 
4992 #if OPT_COLOR_SCHEMES
4993 typedef struct {
4994     char *name;
4995     UINT code;
4996     int fcol;			/* foreground color */
4997     int bcol;			/* background color */
4998     int ccol;			/* cursor color */
4999     int attr;			/* bold, reverse, underline, etc. */
5000     char *list;
5001 } PALETTES;
5002 
5003 static UINT current_scheme;
5004 static UINT num_schemes;
5005 static PALETTES *my_schemes;
5006 static FSM_CHOICES *my_scheme_choices;
5007 
5008 static int
set_scheme_color(FSM_BLIST * fp,int * d,char * s)5009 set_scheme_color(FSM_BLIST * fp, int *d, char *s)
5010 {
5011     int status = TRUE;
5012 
5013     if (fp != 0) {
5014 #if OPT_ENUM_MODES
5015 	int nval;
5016 
5017 	if (isDigit(*s)) {
5018 	    if (!string_to_number(s, &nval)) {
5019 		status = FALSE;
5020 	    } else if (choice_to_name(fp, nval) == 0) {
5021 		*d = ENUM_ILLEGAL;
5022 	    } else {
5023 		*d = nval;
5024 	    }
5025 	} else {
5026 	    *d = choice_to_code(fp, s, strlen(s));
5027 	}
5028 #else
5029 	status = string_to_number(s, d);
5030 #endif
5031     }
5032     return status;
5033 }
5034 
5035 static void
set_scheme_string(char ** d,char * s)5036 set_scheme_string(char **d, char *s)
5037 {
5038     beginDisplay();
5039     if (*d)
5040 	free(*d);
5041     *d = s ? strmalloc(s) : 0;
5042     endofDisplay();
5043 }
5044 
5045 /*
5046  * Find a scheme
5047  */
5048 static PALETTES *
find_scheme(const char * name)5049 find_scheme(const char *name)
5050 {
5051     PALETTES *result = 0, *p;
5052 
5053     if ((p = my_schemes) != 0) {
5054 	while (p->name != 0) {
5055 	    if (!strcmp(p->name, name)) {
5056 		result = p;
5057 		break;
5058 	    }
5059 	    p++;
5060 	}
5061     }
5062     return result;
5063 }
5064 
5065 static PALETTES *
find_scheme_by_code(UINT code)5066 find_scheme_by_code(UINT code)
5067 {
5068     PALETTES *result = 0, *p;
5069 
5070     if ((p = my_schemes) != 0) {
5071 	while (p->name != 0) {
5072 	    if (p->code == code) {
5073 		result = p;
5074 		break;
5075 	    }
5076 	    p++;
5077 	}
5078     }
5079     return result;
5080 }
5081 
5082 /*
5083  * Update the list of choices for color scheme
5084  */
5085 static void
update_scheme_choices(void)5086 update_scheme_choices(void)
5087 {
5088     UINT n;
5089 
5090     if (my_schemes != NULL) {
5091 	beginDisplay();
5092 	if (my_scheme_choices != NULL) {
5093 	    safe_typereallocn(FSM_CHOICES, my_scheme_choices, num_schemes);
5094 	} else {
5095 	    my_scheme_choices = typeallocn(FSM_CHOICES, num_schemes);
5096 	}
5097 	endofDisplay();
5098 
5099 	if (my_scheme_choices != NULL) {
5100 	    for (n = 0; n < num_schemes; n++) {
5101 		my_scheme_choices[n].choice_name = my_schemes[n].name;
5102 		my_scheme_choices[n].choice_code = (int) my_schemes[n].code;
5103 	    }
5104 	    set_fsm_choice(s_color_scheme, my_scheme_choices);
5105 	}
5106     }
5107 }
5108 
5109 static int
same_string(const char * p,const char * q)5110 same_string(const char *p, const char *q)
5111 {
5112     if (p == 0)
5113 	p = "";
5114     if (q == 0)
5115 	q = "";
5116     return !strcmp(p, q);
5117 }
5118 
5119 /*
5120  * Set the current color scheme, given a pointer to its attributes
5121  */
5122 static int
set_current_scheme(PALETTES * p)5123 set_current_scheme(PALETTES * p)
5124 {
5125     PALETTES *q = find_scheme_by_code(current_scheme);
5126 
5127     TRACE((T_CALLED "set_current_scheme()\n"));
5128     current_scheme = p->code;
5129 
5130     if (q != 0
5131 	&& (p->fcol != q->fcol
5132 	    || p->bcol != q->bcol
5133 	    || p->ccol != q->ccol
5134 	    || p->attr != q->attr
5135 	    || !same_string(p->list, q->list))) {
5136 
5137 	if (p->list != 0 && p->list[0] != 0)
5138 	    set_palette(p->list);
5139 
5140 	set_global_g_val(GVAL_FCOLOR, p->fcol);
5141 	term.setfore(p->fcol);
5142 
5143 	set_global_g_val(GVAL_BCOLOR, p->bcol);
5144 	term.setback(p->bcol);
5145 
5146 	set_global_g_val(GVAL_CCOLOR, p->ccol);
5147 	term.setccol(p->ccol);
5148 
5149 	set_global_g_val(GVAL_VIDEO, p->attr);
5150 
5151 	returnCode(TRUE);
5152     }
5153     returnCode(FALSE);
5154 }
5155 
5156 /*
5157  * Remove a color scheme, except for the 'default' scheme.
5158  */
5159 static int
free_scheme(char * name)5160 free_scheme(char *name)
5161 {
5162     PALETTES *p;
5163 
5164     if (strcmp(name, s_default)
5165 	&& (p = find_scheme(name)) != 0) {
5166 	UINT code = p->code;
5167 	PALETTES tofree = *p;
5168 
5169 	TRACE(("free_scheme(%s)\n", name));
5170 
5171 	while (p->name != 0) {
5172 	    p[0] = p[1];
5173 	    p++;
5174 	}
5175 	p->name = 0;
5176 
5177 	beginDisplay();
5178 	free(tofree.name);
5179 	if (tofree.list != 0)
5180 	    free(tofree.list);
5181 	endofDisplay();
5182 
5183 	num_schemes--;
5184 	update_scheme_choices();
5185 
5186 	if (code == current_scheme)
5187 	    (void) set_current_scheme(find_scheme(s_default));
5188 
5189 	return TRUE;
5190     }
5191     TRACE(("cannot free_scheme(%s)\n", name));
5192     return FALSE;
5193 }
5194 
5195 /*
5196  * Allocate a new scheme
5197  */
5198 static PALETTES *
alloc_scheme(const char * name)5199 alloc_scheme(const char *name)
5200 {
5201     static UINT code;
5202     PALETTES *result;
5203 
5204     if ((result = find_scheme(name)) == 0) {
5205 	int len = (int) (++num_schemes);
5206 
5207 	beginDisplay();
5208 	if (len == 1) {
5209 	    len = (int) (++num_schemes);
5210 	    my_schemes = typecallocn(PALETTES, (size_t) len);
5211 	} else {
5212 	    safe_typereallocn(PALETTES, my_schemes, (size_t) len);
5213 	}
5214 	endofDisplay();
5215 
5216 	if (my_schemes == 0) {
5217 	    no_memory("alloc_scheme");
5218 	    return 0;
5219 	}
5220 
5221 	len--;			/* point to list-terminator */
5222 	memset(&my_schemes[len], 0, sizeof(PALETTES));
5223 	while (--len > 0) {
5224 	    my_schemes[len] = my_schemes[len - 1];
5225 	    if (my_schemes[len - 1].name != 0
5226 		&& strcmp(my_schemes[len - 1].name, name) < 0)
5227 		break;
5228 	}
5229 	if ((my_schemes[len].name = strmalloc(name)) != 0) {
5230 	    my_schemes[len].code = code++;	/* unique identifier */
5231 	    my_schemes[len].fcol = -1;
5232 	    my_schemes[len].bcol = -1;
5233 	    my_schemes[len].ccol = -1;
5234 	    my_schemes[len].attr = 0;
5235 	    my_schemes[len].list = 0;
5236 	    result = &my_schemes[len];
5237 	    update_scheme_choices();
5238 	}
5239     }
5240     return result;
5241 }
5242 
5243 static int
scheme_complete(DONE_ARGS)5244 scheme_complete(DONE_ARGS)
5245 {
5246     return kbd_complete(PASS_DONE_ARGS, (const char *) &my_schemes[0],
5247 			sizeof(my_schemes[0]));
5248 }
5249 
5250 static int
prompt_scheme_name(char ** result,int defining)5251 prompt_scheme_name(char **result, int defining)
5252 {
5253     static TBUFF *cbuf;		/* buffer to receive mode name into */
5254     int status;
5255 
5256     /* prompt the user and get an answer */
5257     tb_scopy(&cbuf, "");
5258     if ((status = kbd_reply("name: ",
5259 			    &cbuf,
5260 			    eol_history, ' ',
5261 			    KBD_NORMAL,		/* FIXME: KBD_MAYBEC if !defining */
5262 			    (defining || clexec)
5263 			    ? no_completion
5264 			    : scheme_complete)) == TRUE) {
5265 	/* check for legal name (alphanumeric) */
5266 	if ((status = is_identifier(tb_values(cbuf))) != TRUE) {
5267 	    mlwarn("[Not an identifier: %s]", tb_values(cbuf));
5268 	    return status;
5269 	}
5270 	*result = tb_values(cbuf);
5271 	return status;
5272     }
5273     return status;
5274 }
5275 
5276 /* FIXME: generate this with mktbls? */
5277 /* this table must be sorted, since we do name-completion on it */
5278 /* *INDENT-OFF* */
5279 static const struct VALNAMES scheme_values[] =
5280 {
5281     { s_bcolor,		s_bcolor,	VALTYPE_ENUM,	0 },
5282     { s_ccolor,		s_ccolor,	VALTYPE_ENUM,	0 },
5283     { s_fcolor,		s_fcolor,	VALTYPE_ENUM,	0 },
5284     { s_palette,	s_palette,	VALTYPE_STRING, 0 },
5285     { "use",		"use",		VALTYPE_ENUM,	0 },
5286     { s_video_attrs,	s_video_attrs,	VALTYPE_ENUM,	0 },
5287     { 0, 0, 0, 0 }
5288 };
5289 /* *INDENT-ON* */
5290 
5291 static int
scheme_value_complete(DONE_ARGS)5292 scheme_value_complete(DONE_ARGS)
5293 {
5294     return kbd_complete(PASS_DONE_ARGS,
5295 			(const char *) &scheme_values[0],
5296 			sizeof(scheme_values[0]));
5297 }
5298 
5299 static int
prompt_scheme_value(PALETTES * p)5300 prompt_scheme_value(PALETTES * p)
5301 {
5302     static TBUFF *cbuf;		/* buffer to receive mode name into */
5303     PALETTES *q;
5304     int status;
5305 
5306     /* prompt the user and get an answer */
5307     tb_scopy(&cbuf, "");
5308     if ((status = kbd_reply("scheme value: ",
5309 			    &cbuf,
5310 			    eol_history, '=',
5311 			    KBD_NORMAL,		/* FIXME: KBD_MAYBEC if !defining */
5312 			    scheme_value_complete)) == TRUE) {
5313 	char respbuf[NFILEN];
5314 	char *name = tb_values(cbuf);
5315 	int code = *name;
5316 	int eolchar = (code == *s_palette) ? '\n' : ' ';
5317 	int (*complete) (DONE_ARGS) = no_completion;
5318 #if OPT_ENUM_MODES
5319 	FSM_BLIST *fp = 0;
5320 	const struct VALNAMES *names;
5321 	for (code = 0; scheme_values[code].name; code++) {
5322 	    if (!strcmp(name, scheme_values[code].name)) {
5323 		names = &scheme_values[code];
5324 		if (!strcmp("use", scheme_values[code].name))
5325 		    complete = scheme_complete;
5326 		else if (is_fsm(names))
5327 		    complete = fsm_complete;
5328 		fp = valname_to_choices(names);
5329 		break;
5330 	    }
5331 	}
5332 #else
5333 	void *fp = 0;
5334 #endif
5335 	tb_sappend0(&cbuf, " value: ");
5336 	*respbuf = EOS;
5337 	status = kbd_string(name, respbuf, sizeof(respbuf), eolchar,
5338 			    KBD_NORMAL, complete);
5339 	if (status == TRUE) {
5340 	    mktrimmed(respbuf);
5341 	    if (*name == *s_video_attrs) {
5342 		status = set_scheme_color(fp, &(p->attr), respbuf);
5343 	    } else if (*name == *s_bcolor) {
5344 		status = set_scheme_color(fp, &(p->bcol), respbuf);
5345 	    } else if (*name == *s_ccolor) {
5346 		status = set_scheme_color(fp, &(p->ccol), respbuf);
5347 	    } else if (*name == *s_fcolor) {
5348 		status = set_scheme_color(fp, &(p->fcol), respbuf);
5349 	    } else if (*name == *s_palette) {
5350 		set_scheme_string(&(p->list), respbuf);
5351 		return status;
5352 	    } else if (*name == 'u'
5353 		       && (q = find_scheme(respbuf)) != 0) {
5354 		char *newlist = 0;
5355 		if (q->list == 0 || (newlist = strmalloc(q->list)) != 0) {
5356 		    p->fcol = q->fcol;
5357 		    p->bcol = q->bcol;
5358 		    p->ccol = q->ccol;
5359 		    p->attr = q->attr;
5360 		    if (p->list)
5361 			FreeAndNull(p->list);
5362 		    p->list = newlist;
5363 		} else {
5364 		    return no_memory("prompt_scheme_value");
5365 		}
5366 	    }
5367 	}
5368 	if (status == TRUE)
5369 	    return (end_string() == ' ') ? -1 : TRUE;
5370     }
5371     return status;
5372 }
5373 
5374 #if OPT_UPBUFF
5375 /*ARGSUSED*/
5376 static int
update_schemelist(BUFFER * bp GCC_UNUSED)5377 update_schemelist(BUFFER *bp GCC_UNUSED)
5378 {
5379     return desschemes(FALSE, 1);
5380 }
5381 #endif /* OPT_UPBUFF */
5382 
5383 /*
5384  * Define the default color scheme, based on the current settings of color
5385  * and $palette.
5386  */
5387 void
init_scheme(void)5388 init_scheme(void)
5389 {
5390     PALETTES *p = alloc_scheme(s_default);
5391     char *list = tb_values(tb_curpalette);
5392 
5393     p->fcol = -1;
5394     p->bcol = -1;
5395     p->ccol = -1;
5396     if (list != 0)
5397 	p->list = strmalloc(list);
5398 }
5399 
5400 /*
5401  * Define a color scheme, e.g.,
5402  *	define-color-scheme {name} [fcolor={value}|bcolor={value}|{value}]
5403  * where the {value}'s are all color names or constants 0..NCOLORS-1.
5404  */
5405 /*ARGSUSED*/
5406 int
define_color_scm(int f GCC_UNUSED,int n GCC_UNUSED)5407 define_color_scm(int f GCC_UNUSED, int n GCC_UNUSED)
5408 {
5409     char *name;
5410     int status;
5411 
5412     if ((status = prompt_scheme_name(&name, TRUE)) == TRUE) {
5413 	PALETTES *p = alloc_scheme(name);
5414 	while ((status = prompt_scheme_value(p)) < 0)
5415 	    continue;
5416     }
5417     update_scratch(COLOR_SCHEMES_BufName, update_schemelist);
5418     return status;
5419 }
5420 
5421 /*
5422  * Remove a color scheme
5423  */
5424 /*ARGSUSED*/
5425 int
remove_scheme(int f GCC_UNUSED,int n GCC_UNUSED)5426 remove_scheme(int f GCC_UNUSED, int n GCC_UNUSED)
5427 {
5428     char *name;
5429     int status;
5430 
5431     if (my_schemes == 0 || my_schemes->name == 0) {
5432 	status = FALSE;
5433     } else if ((status = prompt_scheme_name(&name, FALSE)) == TRUE) {
5434 	status = free_scheme(name);
5435     }
5436     update_scratch(COLOR_SCHEMES_BufName, update_schemelist);
5437     return status;
5438 }
5439 
5440 /*ARGSUSED*/
5441 static void
makeschemelist(int dum1 GCC_UNUSED,void * ptr GCC_UNUSED)5442 makeschemelist(int dum1 GCC_UNUSED, void *ptr GCC_UNUSED)
5443 {
5444     static const char fmt[] = "\n   %s=%s";
5445     PALETTES *p;
5446     int num = (int) ((num_schemes > 1) ? (num_schemes - 1) : 0);
5447 
5448     if (my_schemes != 0) {
5449 	bprintf("There %s %d color scheme%s defined",
5450 		(num != 1) ? "are" : "is", num, PLURAL(num));
5451 
5452 	for (p = my_schemes; p != 0 && p->name != 0; p++) {
5453 	    bprintf("\n\n%s", p->name);
5454 	    bprintf(fmt, s_fcolor, get_color_name(p->fcol));
5455 	    bprintf(fmt, s_bcolor, get_color_name(p->bcol));
5456 	    bprintf(fmt, s_ccolor, get_color_name(p->ccol));
5457 	    bprintf(fmt, s_video_attrs,
5458 		    choice_to_name(&fsm_videoattrs_blist, p->attr));
5459 	    if (p->list != 0)
5460 		bprintf(fmt, s_palette, p->list);
5461 	}
5462     }
5463 }
5464 
5465 /*ARGSUSED*/
5466 int
desschemes(int f GCC_UNUSED,int n GCC_UNUSED)5467 desschemes(int f GCC_UNUSED, int n GCC_UNUSED)
5468 {
5469     return liststuff(COLOR_SCHEMES_BufName, FALSE, makeschemelist,
5470 		     0, (void *) 0);
5471 }
5472 
5473 int
chgd_scheme(BUFFER * bp GCC_UNUSED,VALARGS * args,int glob_vals,int testing)5474 chgd_scheme(BUFFER *bp GCC_UNUSED, VALARGS * args, int glob_vals, int testing)
5475 {
5476     if (!testing) {
5477 	PALETTES *p = find_scheme_by_code((UINT) (args->local->vp->i));
5478 	if (p == 0) {
5479 	    return FALSE;
5480 	} else if (set_current_scheme(p)) {
5481 	    set_winflags(glob_vals, WFHARD | WFCOLR);
5482 	    need_update = TRUE;
5483 	}
5484     }
5485     return TRUE;
5486 }
5487 #endif /* OPT_COLOR_SCHEMES */
5488 
5489 /*--------------------------------------------------------------------------*/
5490 #if SYS_VMS
5491 const char *
vms_record_format(int code)5492 vms_record_format(int code)
5493 {
5494     return choice_to_name(&fsm_recordformat_blist, code);
5495 }
5496 
5497 const char *
vms_record_attrs(int code)5498 vms_record_attrs(int code)
5499 {
5500     return choice_to_name(&fsm_recordattrs_blist, code);
5501 }
5502 #endif
5503 
5504 /*--------------------------------------------------------------------------*/
5505 
5506 int
vl_find_mode(const char * name)5507 vl_find_mode(const char *name)
5508 {
5509     return blist_pmatch(&blist_my_mode_list, name, -1);
5510 }
5511 
5512 /*--------------------------------------------------------------------------*/
5513 
5514 #if NO_LEAKS
5515 void
mode_leaks(void)5516 mode_leaks(void)
5517 {
5518     TRACE((T_CALLED "mode_leaks()\n"));
5519 
5520     beginDisplay();
5521 #if OPT_COLOR_SCHEMES
5522     if (my_schemes != 0) {
5523 	int last;
5524 	for (last = 0; my_schemes[last].name != 0; last++) ;
5525 	if (--last >= 0) {
5526 	    while (my_schemes != 0 && my_schemes[last].name != 0) {
5527 		if (!free_scheme(my_schemes[last].name)) {
5528 		    if (last <= 0)
5529 			break;
5530 		    last = 0;
5531 		} else if (last > 0) {
5532 		    last--;
5533 		}
5534 	    }
5535 	}
5536 	if (my_schemes != 0) {	/* it's ok to free the default scheme */
5537 	    FreeAndNull(my_schemes->list);
5538 	    FreeAndNull(my_schemes->name);
5539 	    free(my_schemes);
5540 	}
5541 	FreeAndNull(my_scheme_choices);
5542     }
5543 #endif
5544 
5545 #if OPT_ENUM_MODES && OPT_COLOR
5546     FreeAndNull(my_colors);
5547     FreeAndNull(my_hilite);
5548 #endif
5549 
5550 #if OPT_EVAL || OPT_MAJORMODE
5551     free_my_varmodes();
5552 #endif
5553 
5554 #if OPT_MAJORMODE
5555     while (my_majormodes != 0 && my_majormodes->shortname != 0) {
5556 	char temp[NSTRING];
5557 	free_majormode(vl_strncpy(temp, my_majormodes->shortname, sizeof(temp)));
5558     }
5559     FreeAndNull(my_majormodes);
5560 
5561     FreeAndNull(major_g_vals);
5562     FreeAndNull(major_l_vals);
5563     if (my_mode_list != all_modes
5564 	&& my_mode_list != 0) {
5565 	int j = count_modes();
5566 	while (j > 0)
5567 	    remove_per_major((size_t) j--, my_mode_list[0]);
5568 	FreeAndNull(my_mode_list);
5569     }
5570     FreeAndNull(major_valnames);
5571     FreeAndNull(majormodes_order);
5572 #endif
5573     endofDisplay();
5574 
5575     returnVoid();
5576 }
5577 #endif /* NO_LEAKS */
5578