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