1 /* @(#)coloncmds.c	1.42 20/11/23 Copyright 1986-2020 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)coloncmds.c	1.42 20/11/23 Copyright 1986-2020 J. Schilling";
6 #endif
7 /*
8  *	Commands that deal with ESC : commandline sequences
9  *
10  *	Copyright (c) 1986-2020 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 #include "ved.h"
27 #include <schily/varargs.h>
28 #include "terminal.h"
29 
30 #define	iswhite(c)	((c) == ' ' || (c) == '\t')
31 
32 /*#define	SYMSIZE	31*/
33 #define	SYMSIZE	127
34 
35 LOCAL	Uchar	symbol[SYMSIZE+1];
36 LOCAL	Uchar	*cmdp;
37 LOCAL	int	cmdlen;
38 
39 typedef	void (*function)	__PR((ewin_t *));
40 
41 typedef struct {
42 	Uchar		*c_name;
43 	function	c_func;
44 	int		c_flag;
45 } _CMDTAB, *CMDTAB;
46 
47 EXPORT	void	vcolon		__PR((ewin_t *wp));
48 LOCAL	CMDTAB	lookup		__PR((ewin_t *wp, Uchar* cmd, CMDTAB  cp));
49 LOCAL	BOOL	prefix		__PR((Uchar* pref, Uchar* s));
50 LOCAL	int	getsym		__PR((void));
51 LOCAL	BOOL	get_arg		__PR((ewin_t *wp));
52 LOCAL	BOOL	toint		__PR((ewin_t *wp, int *i));
53 LOCAL	BOOL	getint		__PR((ewin_t *wp, int *i, int low, int high));
54 LOCAL	void	bbind		__PR((ewin_t *wp));
55 LOCAL	void	bhelp		__PR((ewin_t *wp));
56 LOCAL	void	bmacro		__PR((ewin_t *wp));
57 LOCAL	void	bmap		__PR((ewin_t *wp));
58 LOCAL	void	bsmarkwrap	__PR((ewin_t *wp));
59 LOCAL	void	bnext_file	__PR((ewin_t *wp));
60 LOCAL	void	bprev_file	__PR((ewin_t *wp));
61 LOCAL	void	bsetcmd		__PR((ewin_t *wp));
62 LOCAL	void	bsautoindent	__PR((ewin_t *wp));
63 LOCAL	void	bsoptline	__PR((ewin_t *wp));
64 LOCAL	void	bspmargin	__PR((ewin_t *wp));
65 LOCAL	void	bsllen		__PR((ewin_t *wp));
66 LOCAL	void	bsmagic		__PR((ewin_t *wp));
67 LOCAL	void	bspsize		__PR((ewin_t *wp));
68 LOCAL	void	bstab		__PR((ewin_t *wp));
69 LOCAL	void	bstaglen	__PR((ewin_t *wp));
70 LOCAL	void	bstags		__PR((ewin_t *wp));
71 LOCAL	void	bswrapmargin	__PR((ewin_t *wp));
72 LOCAL	void	bsubst		__PR((ewin_t *wp));
73 LOCAL	void	btag		__PR((ewin_t *wp));
74 LOCAL	void	print_status	__PR((ewin_t *wp));
75 LOCAL	void	no_files	__PR((ewin_t *wp));
76 EXPORT	void	printscreen	__PR((ewin_t *wp, char *form, ...));
77 LOCAL	void	printbool	__PR((ewin_t *wp, BOOL  var, char *name));
78 
79 #define	C_BOOL	1
80 
81 LOCAL Uchar *no = UC"no";
82 
83 LOCAL _CMDTAB cmdtab[] = {
84 			{ UC	"backup",	vbackup		},
85 			{ UC	"bind",		bbind		},
86 			{ UC	"help",		bhelp		},
87 			{ UC	"macro",	bmacro		},
88 			{ UC	"map",		bmap		},
89 			{ UC	"next",		bnext_file	},
90 			{ UC	"prev",		bprev_file	},
91 			{ UC	"quit",		vquit		},
92 			{ UC	"set",		bsetcmd		},
93 			{ UC	"substitute",	bsubst		},
94 			{ UC	"tag",		btag		},
95 			{ UC	"vhelp",	vhelp		},
96 			{ UC	NULL,		NULL		}};
97 
98 LOCAL _CMDTAB settab[] = {
99 			{ UC	"autoindent",	bsautoindent,	C_BOOL	},
100 			{ UC	"linelen",	bsllen,		0	},
101 			{ UC	"magic",	bsmagic, 	C_BOOL	},
102 			{ UC	"markwrap",	bsmarkwrap,	C_BOOL	},
103 			{ UC	"optline",	bsoptline,	0	},
104 			{ UC	"pmargin",	bspmargin,	0	},
105 			{ UC	"psize",	bspsize,	0	},
106 			{ UC	"tabstop",	bstab,		0	},
107 			{ UC	"taglength",	bstaglen,	0	},
108 			{ UC	"tags",		bstags,		0	},
109 			{ UC	"wrapmargin",	bswrapmargin,	0	},
110 			{ UC	NULL,		NULL		}};
111 
112 /*
113  * Parse and execute a colon command
114  */
115 EXPORT void
vcolon(wp)116 vcolon(wp)
117 	ewin_t	*wp;
118 {
119 	register	CMDTAB	cp;
120 			Uchar	cmdline[NAMESIZE];
121 
122 	if ((cmdlen = getcmdline(wp, cmdline, sizeof (cmdline), ":")) == 0)
123 		return;
124 	cmdp = cmdline;
125 	if (!getsym()) {
126 		abortmsg(wp);
127 	} else if ((cp = lookup(wp, symbol, cmdtab)) != NULL) {
128 		(*cp->c_func)(wp);
129 	}
130 }
131 
132 /*
133  * Lookup command in command tab
134  */
135 LOCAL CMDTAB
lookup(wp,cmd,cp)136 lookup(wp, cmd, cp)
137 		ewin_t	*wp;
138 		Uchar	*cmd;
139 	register CMDTAB	cp;
140 {
141 			CMDTAB	ocp = cp;
142 	register	CMDTAB	found = NULL;
143 			BOOL	first = TRUE;
144 
145 again:
146 	for (; cp->c_name; cp++) {
147 		if (prefix(cmd, cp->c_name)) {
148 			if (!found) {
149 				found = cp;
150 			} else {
151 				writeerr(wp, "%s: AMBIGOUS", cmd);
152 				return (NULL);
153 			}
154 		}
155 	}
156 	if (!first && found && !(found->c_flag & C_BOOL))
157 		found = NULL;
158 	if (!found) {
159 		if (first && prefix(no, cmd)) {
160 			first = FALSE;
161 			cmd = &cmd[2];
162 			cp = ocp;
163 			goto again;
164 		}
165 		writeerr(wp, "%s: UNKNOWN", cmd);
166 	}
167 	return (found);
168 }
169 
170 /*
171  * Check if a string starts with a given prefix
172  */
173 LOCAL BOOL
prefix(pref,s)174 prefix(pref, s)
175 	Uchar	*pref;
176 	Uchar	*s;
177 {
178 	while (*pref) {
179 		if (*pref++ != *s++)
180 			return (FALSE);
181 	}
182 	return (TRUE);
183 }
184 
185 /*
186  * Get next symbol (word)
187  */
188 LOCAL int
getsym()189 getsym()
190 {
191 	register Uchar	*r_cmd = cmdp;
192 	register Uchar	*r_sym = symbol;
193 	register int	i;
194 
195 	while (*r_cmd && iswhite(*r_cmd))
196 		r_cmd++;
197 	for (i = 0; *r_cmd && !iswhite(*r_cmd) && i < SYMSIZE; i++)
198 		*r_sym++ = *r_cmd++;
199 	*r_sym = '\0';
200 	cmdlen -= r_cmd - cmdp;
201 	cmdp = r_cmd;
202 	return (i);
203 }
204 
205 /*
206  * Get next symbol (word) and check if one exists
207  */
208 LOCAL BOOL
get_arg(wp)209 get_arg(wp)
210 	ewin_t	*wp;
211 {
212 	if (!getsym()) {
213 		writeerr(wp, "NO ARG");
214 		return (FALSE);
215 	}
216 	return (TRUE);
217 }
218 
219 /*
220  * Get integer number, check if valid
221  */
222 LOCAL BOOL
toint(wp,i)223 toint(wp, i)
224 	ewin_t	*wp;
225 	int	*i;
226 {
227 	if (*astoi(C symbol, i) != '\0') {
228 		writeerr(wp, "NOT A NUMBER: %s", symbol);
229 		return (FALSE);
230 	}
231 	return (TRUE);
232 }
233 
234 /*
235  * Get integer number with bounds
236  */
237 LOCAL BOOL
getint(wp,i,low,high)238 getint(wp, i, low, high)
239 	ewin_t	*wp;
240 	int	*i;
241 	int	low;
242 	int	high;
243 {
244 	if (!get_arg(wp))
245 		return (FALSE);
246 
247 	if (toint(wp, i)) {
248 		if (*i < low || *i >= high) {
249 			writeerr(wp, "BAD ARG: %d", *i);
250 			return (FALSE);
251 		}
252 		return (TRUE);
253 	}
254 	return (FALSE);
255 }
256 
257 /*
258  * Run bind command
259  */
260 LOCAL void
bbind(wp)261 bbind(wp)
262 	ewin_t	*wp;
263 {
264 	bindcmd(wp, cmdp, cmdlen);
265 }
266 
267 /*
268  * Give online help for colon commands
269  */
270 LOCAL void
bhelp(wp)271 bhelp(wp)
272 	ewin_t	*wp;
273 {
274 	register	CMDTAB	cp;
275 
276 	MOVE_CURSOR_ABS(wp, 1, 0);
277 	printscreen(wp, "Available Commands are:\n");
278 	for (cp = cmdtab; cp->c_name; cp++)
279 		printscreen(wp, "%s\n", cp->c_name);
280 	wait_for_confirm(wp);
281 	vredisp(wp);
282 }
283 
284 char mstr[128];
285 
286 /*
287  * Set temporary macro (call with ESC *)
288  */
289 /* ARGSUSED */
290 LOCAL void
bmacro(wp)291 bmacro(wp)
292 	ewin_t	*wp;
293 {
294 	strncpy(mstr, C &cmdp[1], sizeof (mstr));
295 	mstr[sizeof (mstr)-1] = '\0';
296 }
297 
298 /*
299  * List current mappings
300  */
301 LOCAL void
bmap(wp)302 bmap(wp)
303 	ewin_t	*wp;
304 {
305 	MOVE_CURSOR_ABS(wp, 1, 0);
306 	list_map(wp);
307 	wait_for_confirm(wp);
308 	vredisp(wp);
309 }
310 
311 /*
312  * Set markwrap/no-markwrap
313  */
314 LOCAL void
bsmarkwrap(wp)315 bsmarkwrap(wp)
316 	ewin_t	*wp;
317 {
318 	BOOL	save = wp->markwrap;
319 
320 	wp->markwrap = !prefix(no, symbol);
321 	if (save != wp->markwrap) {
322 		wp->llen += wp->markwrap ? -1 : 1;
323 		vredisp(wp);
324 		setpos(wp);
325 	}
326 }
327 
328 /*
329  * Change to next file in argument list
330  */
331 LOCAL void
bnext_file(wp)332 bnext_file(wp)
333 	ewin_t	*wp;
334 {
335 	if (fileidx >= nfiles-1)
336 		no_files(wp);
337 	else if (!change_file(wp, files[++fileidx]))
338 		--fileidx;
339 	else
340 		newwindow(wp);
341 }
342 
343 /*
344  * Change to previous file in argument list
345  */
346 LOCAL void
bprev_file(wp)347 bprev_file(wp)
348 	ewin_t	*wp;
349 {
350 	if (fileidx <= 0)
351 		no_files(wp);
352 	else if (!change_file(wp, files[--fileidx]))
353 		++fileidx;
354 	else
355 		newwindow(wp);
356 }
357 
358 /*
359  * Set function that calls other sub-set function
360  */
361 LOCAL void
bsetcmd(wp)362 bsetcmd(wp)
363 	ewin_t	*wp;
364 {
365 	register	CMDTAB	cp;
366 
367 	if (!getsym())
368 		print_status(wp);
369 	else if ((cp = lookup(wp, symbol, settab)) != NULL)
370 		(*cp->c_func)(wp);
371 }
372 
373 /*
374  * Set auto-indent/no-auto-indent
375  */
376 LOCAL void
bsautoindent(wp)377 bsautoindent(wp)
378 	ewin_t	*wp;
379 {
380 	wp->autoindent = !prefix(no, symbol);
381 }
382 
383 /*
384  * Set optimal line for screen adjust
385  */
386 LOCAL void
bsoptline(wp)387 bsoptline(wp)
388 	ewin_t	*wp;
389 {
390 	int	i;
391 
392 	if (getint(wp, &i, 1, wp->psize - wp->pmargin))
393 		wp->optline = i;
394 }
395 
396 /*
397  * Set page-margin.
398  * This sets the number of lines the curser must stay away from
399  * the top or bottom of the screen.
400  */
401 LOCAL void
bspmargin(wp)402 bspmargin(wp)
403 	ewin_t	*wp;
404 {
405 	int	i;
406 
407 	if (getint(wp, &i, 0, min(wp->psize/2, wp->optline)))
408 		wp->pmargin = i;
409 }
410 
411 /*
412  * Set line-len
413  */
414 LOCAL void
bsllen(wp)415 bsllen(wp)
416 	ewin_t	*wp;
417 {
418 	int	save = wp->llen;
419 
420 	if (getint(wp, &wp->llen, 1, 1000) && wp->llen != save) {
421 		vredisp(wp);
422 		setpos(wp);
423 	}
424 }
425 
426 /*
427  * Set magic/no-magic
428  */
429 LOCAL void
bsmagic(wp)430 bsmagic(wp)
431 	ewin_t	*wp;
432 {
433 	wp->magic = !prefix(no, symbol);
434 }
435 
436 /*
437  * Set page-size
438  */
439 LOCAL void
bspsize(wp)440 bspsize(wp)
441 	ewin_t	*wp;
442 {
443 	int	save = wp->psize;
444 
445 	if (getint(wp, &wp->psize, 1, 1000) && wp->psize != save) {
446 		if (wp->optline == save/2 || wp->optline > wp->psize)
447 			wp->optline = wp->psize/2;
448 		if (wp->pmargin > min(wp->psize/2, wp->optline))
449 			wp->pmargin = 0;
450 		vredisp(wp);
451 		setpos(wp);
452 	}
453 }
454 
455 /*
456  * Set width of a tab
457  */
458 LOCAL void
bstab(wp)459 bstab(wp)
460 	ewin_t	*wp;
461 {
462 	int	save = wp->tabstop;
463 
464 	if (getint(wp, &wp->tabstop, 1, wp->llen) && wp->tabstop != save) {
465 		vredisp(wp);
466 		setpos(wp);
467 	}
468 }
469 
470 /*
471  * Set valig tagstring length
472  */
473 LOCAL void
bstaglen(wp)474 bstaglen(wp)
475 	ewin_t	*wp;
476 {
477 	int	i;
478 
479 	if (getint(wp, &i, 0, 100) && i >= 0)
480 		taglength = i;
481 }
482 
483 /*
484  * Set tag database search path
485  */
486 LOCAL void
bstags(wp)487 bstags(wp)
488 	ewin_t	*wp;
489 {
490 	if (!get_arg(wp))
491 		return;
492 
493 	strncpy(C tags, C symbol, NAMESIZE);
494 	tags[NAMESIZE-1] = '\0';
495 }
496 
497 /*
498  * Set auto-wrapmargin value
499  */
500 LOCAL void
bswrapmargin(wp)501 bswrapmargin(wp)
502 	ewin_t	*wp;
503 {
504 	getint(wp, &wp->wrapmargin, 0, wp->llen);
505 }
506 
507 /*
508  * Run substitute command
509  */
510 LOCAL void
bsubst(wp)511 bsubst(wp)
512 	ewin_t	*wp;
513 {
514 	subst(wp, cmdp, cmdlen);
515 }
516 
517 /*
518  * Go to tag that was specified on command line
519  */
520 LOCAL void
btag(wp)521 btag(wp)
522 	ewin_t	*wp;
523 {
524 	if (!get_arg(wp))
525 		return;
526 
527 	gototag(wp, symbol);
528 }
529 
530 /*
531  * Print a summary of the values of all variables
532  */
533 LOCAL void
print_status(wp)534 print_status(wp)
535 	ewin_t	*wp;
536 {
537 	MOVE_CURSOR_ABS(wp, 1, 0);
538 	printscreen(wp, "psize: %-10d linelen: %-10d optline: %-10d\n",
539 						wp->psize, wp->llen, wp->optline);
540 	printscreen(wp, "pmargin: %d\n", wp->pmargin);
541 	printscreen(wp, "wrapmargin: %d\n", wp->wrapmargin);
542 	printscreen(wp, "maxlinelen: %d\n", wp->maxlinelen);
543 	printscreen(wp, "tabstop: %d\n", wp->tabstop);
544 	printbool(wp, wp->raw8, "raw8");
545 	printscreen(wp, "pid: %d\n", pid);
546 	printscreen(wp, "modflg: %ld\n", wp->modflg);
547 	printscreen(wp, "curnum: %lld\n", (Llong)wp->curnum);
548 /*	printscreen(wp, "mult: %d\n", mult);*/
549 	printscreen(wp, "cursor.hp: %d(%d) cursor.vp: %d(%d)\n",
550 		cursor.hp, realhp(wp, &cursor), cursor.vp, realvp(wp, &cursor));
551 	printscreen(wp, "window: %lld\n", (Llong)wp->window);
552 	printscreen(wp, "ewindow: %lld\n", (Llong)wp->ewindow);
553 	printscreen(wp, "dot: %lld\n", (Llong)wp->dot);
554 	printscreen(wp, "eof: %lld\n", (Llong)wp->eof);
555 	printscreen(wp, "mark: %lld\n", (Llong)wp->mark);
556 	printbool(wp, wp->markvalid, "markvalid");
557 	printbool(wp, wp->autoindent, "autoindent");
558 	if ((wp->eflags & FREADONLY) != 0)
559 		printscreen(wp, "readonly (locked by other program)\n");
560 	else
561 		printbool(wp, ReadOnly, "readonly");
562 	printbool(wp, !nobak, "bak");
563 	printbool(wp, !noedtmp, "edtmp");
564 	printbool(wp, recover, "recover");
565 	printbool(wp, wp->magic, "magic");
566 	printscreen(wp, wp->overstrikemode ? "overstrikemode\n" : "insertmode\n");
567 	printbool(wp, wp->visible, "visible");
568 	printbool(wp, wp->markwrap, "markwrap");
569 	printscreen(wp, "taglength: %d tags: '%s'\n", taglength, tags);
570 	wait_for_confirm(wp);
571 	vredisp(wp);
572 }
573 
574 /*
575  * Print warning at end of ESC : next/prev file list
576  */
577 LOCAL void
no_files(wp)578 no_files(wp)
579 	ewin_t	*wp;
580 {
581 	writeerr(wp, "NO MORE FILES");
582 }
583 
584 /*
585  * Print a line on screen, wait if the maximum
586  * numbers of lines on screen is reached
587  */
588 /* PRINTFLIKE2 */
589 #ifdef	PROTOTYPES
590 EXPORT void
printscreen(ewin_t * wp,char * form,...)591 printscreen(ewin_t *wp, char *form, ...)
592 #else
593 EXPORT void
594 printscreen(wp, form, va_alist)
595 	ewin_t	*wp;
596 	char	*form;
597 	va_dcl
598 #endif
599 {
600 	va_list	args;
601 	Uchar	temp[NAMESIZE];
602 	Uchar	tform[NAMESIZE];
603 	int	slen;
604 	BOOL	nl	= FALSE;
605 
606 	if (cpos.vp >= wp->psize) {
607 		wait_continue(wp);
608 		MOVE_CURSOR_ABS(wp, 1, 0);
609 	}
610 	strncpy(C tform, form, NAMESIZE);
611 	tform[NAMESIZE-1] = '\0';
612 	slen = strlen(C tform);
613 	if (tform[slen-1] == '\n') {
614 		nl = TRUE;
615 		tform[slen-1] = '\0';
616 	}
617 #ifdef	PROTOTYPES
618 	va_start(args, form);
619 #else
620 	va_start(args);
621 #endif
622 	snprintf(C temp, sizeof (temp), "%r", tform, args);
623 	va_end(args);
624 	output(temp);
625 	CLEAR_TO_EOF_LINE(wp);
626 	if (nl) {
627 		addchar('\n');
628 	}
629 }
630 
631 /*
632  * Print a boolean value
633  */
634 LOCAL void
printbool(wp,var,name)635 printbool(wp, var, name)
636 	ewin_t	*wp;
637 	BOOL	var;
638 	char	*name;
639 {
640 	printscreen(wp, "%s%s\n", var ? "" : "no", name);
641 }
642