Merge from vendor branch AWK:
[dragonfly.git] / contrib / nvi / cl / cl_funcs.c
1 /*-
2  * Copyright (c) 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 1993, 1994, 1995, 1996
5  *      Keith Bostic.  All rights reserved.
6  *
7  * See the LICENSE file for redistribution information.
8  */
9
10 #include "config.h"
11
12 #ifndef lint
13 static const char sccsid[] = "@(#)cl_funcs.c    10.50 (Berkeley) 9/24/96";
14 #endif /* not lint */
15
16 #include <sys/types.h>
17 #include <sys/queue.h>
18 #include <sys/time.h>
19
20 #include <bitstring.h>
21 #include <ctype.h>
22 #include <curses.h>
23 #include <signal.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <termios.h>
28 #include <unistd.h>
29
30 #include "../common/common.h"
31 #include "../vi/vi.h"
32 #include "cl.h"
33
34 /*
35  * cl_addstr --
36  *      Add len bytes from the string at the cursor, advancing the cursor.
37  *
38  * PUBLIC: int cl_addstr __P((SCR *, const char *, size_t));
39  */
40 int
41 cl_addstr(sp, str, len)
42         SCR *sp;
43         const char *str;
44         size_t len;
45 {
46         CL_PRIVATE *clp;
47         size_t oldy, oldx;
48         int iv;
49
50         clp = CLP(sp);
51
52         /*
53          * If ex isn't in control, it's the last line of the screen and
54          * it's a split screen, use inverse video.
55          */
56         iv = 0;
57         getyx(stdscr, oldy, oldx);
58         if (!F_ISSET(sp, SC_SCR_EXWROTE) &&
59             oldy == RLNO(sp, LASTLINE(sp)) && IS_SPLIT(sp)) {
60                 iv = 1;
61                 (void)standout();
62         }
63
64         if (addnstr(str, len) == ERR)
65                 return (1);
66
67         if (iv)
68                 (void)standend();
69         return (0);
70 }
71
72 /*
73  * cl_attr --
74  *      Toggle a screen attribute on/off.
75  *
76  * PUBLIC: int cl_attr __P((SCR *, scr_attr_t, int));
77  */
78 int
79 cl_attr(sp, attribute, on)
80         SCR *sp;
81         scr_attr_t attribute;
82         int on;
83 {
84         CL_PRIVATE *clp;
85
86         clp = CLP(sp);
87
88         switch (attribute) {
89         case SA_ALTERNATE:
90         /*
91          * !!!
92          * There's a major layering violation here.  The problem is that the
93          * X11 xterm screen has what's known as an "alternate" screen.  Some
94          * xterm termcap/terminfo entries include sequences to switch to/from
95          * that alternate screen as part of the ti/te (smcup/rmcup) strings.
96          * Vi runs in the alternate screen, so that you are returned to the
97          * same screen contents on exit from vi that you had when you entered
98          * vi.  Further, when you run :shell, or :!date or similar ex commands,
99          * you also see the original screen contents.  This wasn't deliberate
100          * on vi's part, it's just that it historically sent terminal init/end
101          * sequences at those times, and the addition of the alternate screen
102          * sequences to the strings changed the behavior of vi.  The problem
103          * caused by this is that we don't want to switch back to the alternate
104          * screen while getting a new command from the user, when the user is
105          * continuing to enter ex commands, e.g.:
106          *
107          *      :!date                          <<< switch to original screen
108          *      [Hit return to continue]        <<< prompt user to continue
109          *      :command                        <<< get command from user
110          *
111          * Note that the :command input is a true vi input mode, e.g., input
112          * maps and abbreviations are being done.  So, we need to be able to
113          * switch back into the vi screen mode, without flashing the screen. 
114          *
115          * To make matters worse, the curses initscr() and endwin() calls will
116          * do this automatically -- so, this attribute isn't as controlled by
117          * the higher level screen as closely as one might like.
118          */
119         if (on) {
120                 if (clp->ti_te != TI_SENT) {
121                         clp->ti_te = TI_SENT;
122                         if (clp->smcup == NULL)
123                                 (void)cl_getcap(sp, "smcup", &clp->smcup);
124                         if (clp->smcup != NULL)
125                                 (void)tputs(clp->smcup, 1, cl_putchar);
126                 }
127         } else
128                 if (clp->ti_te != TE_SENT) {
129                         clp->ti_te = TE_SENT;
130                         if (clp->rmcup == NULL)
131                                 (void)cl_getcap(sp, "rmcup", &clp->rmcup);
132                         if (clp->rmcup != NULL)
133                                 (void)tputs(clp->rmcup, 1, cl_putchar);
134                         (void)fflush(stdout);
135                 }
136                 (void)fflush(stdout);
137                 break;
138         case SA_INVERSE:
139                 if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) {
140                         if (clp->smso == NULL)
141                                 return (1);
142                         if (on)
143                                 (void)tputs(clp->smso, 1, cl_putchar);
144                         else
145                                 (void)tputs(clp->rmso, 1, cl_putchar);
146                         (void)fflush(stdout);
147                 } else {
148                         if (on)
149                                 (void)standout();
150                         else
151                                 (void)standend();
152                 }
153                 break;
154         default:
155                 abort();
156         }
157         return (0);
158 }
159
160 /*
161  * cl_baud --
162  *      Return the baud rate.
163  *
164  * PUBLIC: int cl_baud __P((SCR *, u_long *));
165  */
166 int
167 cl_baud(sp, ratep)
168         SCR *sp;
169         u_long *ratep;
170 {
171         CL_PRIVATE *clp;
172
173         /*
174          * XXX
175          * There's no portable way to get a "baud rate" -- cfgetospeed(3)
176          * returns the value associated with some #define, which we may
177          * never have heard of, or which may be a purely local speed.  Vi
178          * only cares if it's SLOW (w300), slow (w1200) or fast (w9600).
179          * Try and detect the slow ones, and default to fast.
180          */
181         clp = CLP(sp);
182         switch (cfgetospeed(&clp->orig)) {
183         case B50:
184         case B75:
185         case B110:
186         case B134:
187         case B150:
188         case B200:
189         case B300:
190         case B600:
191                 *ratep = 600;
192                 break;
193         case B1200:
194                 *ratep = 1200;
195                 break;
196         default:
197                 *ratep = 9600;
198                 break;
199         }
200         return (0);
201 }
202
203 /*
204  * cl_bell --
205  *      Ring the bell/flash the screen.
206  *
207  * PUBLIC: int cl_bell __P((SCR *));
208  */
209 int
210 cl_bell(sp)
211         SCR *sp;
212 {
213         if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE))
214                 (void)write(STDOUT_FILENO, "\07", 1);           /* \a */
215         else {
216                 /*
217                  * Vi has an edit option which determines if the terminal
218                  * should be beeped or the screen flashed.
219                  */
220                 if (O_ISSET(sp, O_FLASH))
221                         (void)flash();
222                 else
223                         (void)beep();
224         }
225         return (0);
226 }
227
228 /*
229  * cl_clrtoeol --
230  *      Clear from the current cursor to the end of the line.
231  *
232  * PUBLIC: int cl_clrtoeol __P((SCR *));
233  */
234 int
235 cl_clrtoeol(sp)
236         SCR *sp;
237 {
238         return (clrtoeol() == ERR);
239 }
240
241 /*
242  * cl_cursor --
243  *      Return the current cursor position.
244  *
245  * PUBLIC: int cl_cursor __P((SCR *, size_t *, size_t *));
246  */
247 int
248 cl_cursor(sp, yp, xp)
249         SCR *sp;
250         size_t *yp, *xp;
251 {
252         /*
253          * The curses screen support splits a single underlying curses screen
254          * into multiple screens to support split screen semantics.  For this
255          * reason the returned value must be adjusted to be relative to the
256          * current screen, and not absolute.  Screens that implement the split
257          * using physically distinct screens won't need this hack.
258          */
259         getyx(stdscr, *yp, *xp);
260         *yp -= sp->woff;
261         return (0);
262 }
263
264 /*
265  * cl_deleteln --
266  *      Delete the current line, scrolling all lines below it.
267  *
268  * PUBLIC: int cl_deleteln __P((SCR *));
269  */
270 int
271 cl_deleteln(sp)
272         SCR *sp;
273 {
274         CHAR_T ch;
275         CL_PRIVATE *clp;
276         size_t col, lno, spcnt, oldy, oldx;
277
278         clp = CLP(sp);
279
280         /*
281          * This clause is required because the curses screen uses reverse
282          * video to delimit split screens.  If the screen does not do this,
283          * this code won't be necessary.
284          *
285          * If the bottom line was in reverse video, rewrite it in normal
286          * video before it's scrolled.
287          *
288          * Check for the existence of a chgat function; XSI requires it, but
289          * historic implementations of System V curses don't.   If it's not
290          * a #define, we'll fall back to doing it by hand, which is slow but
291          * acceptable.
292          *
293          * By hand means walking through the line, retrieving and rewriting
294          * each character.  Curses has no EOL marker, so track strings of
295          * spaces, and copy the trailing spaces only if there's a non-space
296          * character following.
297          */
298         if (!F_ISSET(sp, SC_SCR_EXWROTE) && IS_SPLIT(sp)) {
299                 getyx(stdscr, oldy, oldx);
300 #ifdef mvchgat
301                 mvchgat(RLNO(sp, LASTLINE(sp)), 0, -1, A_NORMAL, 0, NULL);
302 #else
303                 for (lno = RLNO(sp, LASTLINE(sp)), col = spcnt = 0;;) {
304                         (void)move(lno, col);
305                         ch = winch(stdscr);
306                         if (isblank(ch))
307                                 ++spcnt;
308                         else {
309                                 (void)move(lno, col - spcnt);
310                                 for (; spcnt > 0; --spcnt)
311                                         (void)addch(' ');
312                                 (void)addch(ch);
313                         }
314                         if (++col >= sp->cols)
315                                 break;
316                 }
317 #endif
318                 (void)move(oldy, oldx);
319         }
320
321         /*
322          * The bottom line is expected to be blank after this operation,
323          * and other screens must support that semantic.
324          */
325         return (deleteln() == ERR);
326 }
327
328 /* 
329  * cl_ex_adjust --
330  *      Adjust the screen for ex.  This routine is purely for standalone
331  *      ex programs.  All special purpose, all special case.
332  *
333  * PUBLIC: int cl_ex_adjust __P((SCR *, exadj_t));
334  */
335 int
336 cl_ex_adjust(sp, action)
337         SCR *sp;
338         exadj_t action;
339 {
340         CL_PRIVATE *clp;
341         int cnt;
342
343         clp = CLP(sp);
344         switch (action) {
345         case EX_TERM_SCROLL:
346                 /* Move the cursor up one line if that's possible. */
347                 if (clp->cuu1 != NULL)
348                         (void)tputs(clp->cuu1, 1, cl_putchar);
349                 else if (clp->cup != NULL)
350                         (void)tputs(tgoto(clp->cup,
351                             0, LINES - 2), 1, cl_putchar);
352                 else
353                         return (0);
354                 /* FALLTHROUGH */
355         case EX_TERM_CE:
356                 /* Clear the line. */
357                 if (clp->el != NULL) {
358                         (void)putchar('\r');
359                         (void)tputs(clp->el, 1, cl_putchar);
360                 } else {
361                         /*
362                          * Historically, ex didn't erase the line, so, if the
363                          * displayed line was only a single glyph, and <eof>
364                          * was more than one glyph, the output would not fully
365                          * overwrite the user's input.  To fix this, output
366                          * the maxiumum character number of spaces.  Note,
367                          * this won't help if the user entered extra prompt
368                          * or <blank> characters before the command character.
369                          * We'd have to do a lot of work to make that work, and
370                          * it's almost certainly not worth the effort.
371                          */
372                         for (cnt = 0; cnt < MAX_CHARACTER_COLUMNS; ++cnt)
373                                 (void)putchar('\b');
374                         for (cnt = 0; cnt < MAX_CHARACTER_COLUMNS; ++cnt)
375                                 (void)putchar(' ');
376                         (void)putchar('\r');
377                         (void)fflush(stdout);
378                 }
379                 break;
380         default:
381                 abort();
382         }
383         return (0);
384 }
385
386 /*
387  * cl_insertln --
388  *      Push down the current line, discarding the bottom line.
389  *
390  * PUBLIC: int cl_insertln __P((SCR *));
391  */
392 int
393 cl_insertln(sp)
394         SCR *sp;
395 {
396         /*
397          * The current line is expected to be blank after this operation,
398          * and the screen must support that semantic.
399          */
400         return (insertln() == ERR);
401 }
402
403 /*
404  * cl_keyval --
405  *      Return the value for a special key.
406  *
407  * PUBLIC: int cl_keyval __P((SCR *, scr_keyval_t, CHAR_T *, int *));
408  */
409 int
410 cl_keyval(sp, val, chp, dnep)
411         SCR *sp;
412         scr_keyval_t val;
413         CHAR_T *chp;
414         int *dnep;
415 {
416         CL_PRIVATE *clp;
417
418         /*
419          * VEOF, VERASE and VKILL are required by POSIX 1003.1-1990,
420          * VWERASE is a 4BSD extension.
421          */
422         clp = CLP(sp);
423         switch (val) {
424         case KEY_VEOF:
425                 *dnep = (*chp = clp->orig.c_cc[VEOF]) == _POSIX_VDISABLE;
426                 break;
427         case KEY_VERASE:
428                 *dnep = (*chp = clp->orig.c_cc[VERASE]) == _POSIX_VDISABLE;
429                 break;
430         case KEY_VKILL:
431                 *dnep = (*chp = clp->orig.c_cc[VKILL]) == _POSIX_VDISABLE;
432                 break;
433 #ifdef VWERASE
434         case KEY_VWERASE:
435                 *dnep = (*chp = clp->orig.c_cc[VWERASE]) == _POSIX_VDISABLE;
436                 break;
437 #endif
438         default:
439                 *dnep = 1;
440                 break;
441         }
442         return (0);
443 }
444
445 /*
446  * cl_move --
447  *      Move the cursor.
448  *
449  * PUBLIC: int cl_move __P((SCR *, size_t, size_t));
450  */
451 int
452 cl_move(sp, lno, cno)
453         SCR *sp;
454         size_t lno, cno;
455 {
456         /* See the comment in cl_cursor. */
457         if (move(RLNO(sp, lno), cno) == ERR) {
458                 msgq(sp, M_ERR,
459                     "Error: move: l(%u) c(%u) o(%u)", lno, cno, sp->woff);
460                 return (1);
461         }
462         return (0);
463 }
464
465 /*
466  * cl_refresh --
467  *      Refresh the screen.
468  *
469  * PUBLIC: int cl_refresh __P((SCR *, int));
470  */
471 int
472 cl_refresh(sp, repaint)
473         SCR *sp;
474         int repaint;
475 {
476         CL_PRIVATE *clp;
477
478         clp = CLP(sp);
479
480         /*
481          * If we received a killer signal, we're done, there's no point
482          * in refreshing the screen.
483          */
484         if (clp->killersig)
485                 return (0);
486
487         /*
488          * If repaint is set, the editor is telling us that we don't know
489          * what's on the screen, so we have to repaint from scratch.
490          *
491          * In the curses library, doing wrefresh(curscr) is okay, but the
492          * screen flashes when we then apply the refresh() to bring it up
493          * to date.  So, use clearok().
494          */
495         if (repaint)
496                 clearok(curscr, 1);
497         return (refresh() == ERR);
498 }
499
500 /*
501  * cl_rename --
502  *      Rename the file.
503  *
504  * PUBLIC: int cl_rename __P((SCR *, char *, int));
505  */
506 int
507 cl_rename(sp, name, on)
508         SCR *sp;
509         char *name;
510         int on;
511 {
512         GS *gp;
513         CL_PRIVATE *clp;
514         char *ttype;
515
516         gp = sp->gp;
517         clp = CLP(sp);
518
519         ttype = OG_STR(gp, GO_TERM);
520
521         /*
522          * XXX
523          * We can only rename windows for xterm.
524          */
525         if (on) {
526                 if (F_ISSET(clp, CL_RENAME_OK) &&
527                     !strncmp(ttype, "xterm", sizeof("xterm") - 1)) {
528                         F_SET(clp, CL_RENAME);
529                         (void)printf(XTERM_RENAME, name);
530                         (void)fflush(stdout);
531                 }
532         } else
533                 if (F_ISSET(clp, CL_RENAME)) {
534                         F_CLR(clp, CL_RENAME);
535                         (void)printf(XTERM_RENAME, ttype);
536                         (void)fflush(stdout);
537                 }
538         return (0);
539 }
540
541 /*
542  * cl_suspend --
543  *      Suspend a screen.
544  *
545  * PUBLIC: int cl_suspend __P((SCR *, int *));
546  */
547 int
548 cl_suspend(sp, allowedp)
549         SCR *sp;
550         int *allowedp;
551 {
552         struct termios t;
553         CL_PRIVATE *clp;
554         GS *gp;
555         size_t oldy, oldx;
556         int changed;
557
558         gp = sp->gp;
559         clp = CLP(sp);
560         *allowedp = 1;
561
562         /*
563          * The ex implementation of this function isn't needed by screens not
564          * supporting ex commands that require full terminal canonical mode
565          * (e.g. :suspend).
566          *
567          * The vi implementation of this function isn't needed by screens not
568          * supporting vi process suspension, i.e. any screen that isn't backed
569          * by a UNIX shell.
570          *
571          * Setting allowedp to 0 will cause the editor to reject the command.
572          */
573         if (F_ISSET(sp, SC_EX)) { 
574                 /* Save the terminal settings, and restore the original ones. */
575                 if (F_ISSET(clp, CL_STDIN_TTY)) {
576                         (void)tcgetattr(STDIN_FILENO, &t);
577                         (void)tcsetattr(STDIN_FILENO,
578                             TCSASOFT | TCSADRAIN, &clp->orig);
579                 }
580
581                 /* Stop the process group. */
582                 (void)kill(0, SIGTSTP);
583
584                 /* Time passes ... */
585
586                 /* Restore terminal settings. */
587                 if (F_ISSET(clp, CL_STDIN_TTY))
588                         (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t);
589                 return (0);
590         }
591
592         /*
593          * Move to the lower left-hand corner of the screen.
594          *
595          * XXX
596          * Not sure this is necessary in System V implementations, but it
597          * shouldn't hurt.
598          */
599         getyx(stdscr, oldy, oldx);
600         (void)move(LINES - 1, 0);
601         (void)refresh();
602
603         /*
604          * Temporarily end the screen.  System V introduced a semantic where
605          * endwin() could be restarted.  We use it because restarting curses
606          * from scratch often fails in System V.  4BSD curses didn't support
607          * restarting after endwin(), so we have to do what clean up we can
608          * without calling it.
609          */
610 #ifdef HAVE_BSD_CURSES
611         /* Save the terminal settings. */
612         (void)tcgetattr(STDIN_FILENO, &t);
613 #endif
614
615         /* Restore the cursor keys to normal mode. */
616         (void)keypad(stdscr, FALSE);
617
618         /* Restore the window name. */
619         (void)cl_rename(sp, NULL, 0);
620
621 #ifdef HAVE_BSD_CURSES
622         (void)cl_attr(sp, SA_ALTERNATE, 0);
623 #else
624         (void)endwin();
625 #endif
626         /*
627          * XXX
628          * Restore the original terminal settings.  This is bad -- the
629          * reset can cause character loss from the tty queue.  However,
630          * we can't call endwin() in BSD curses implementations, and too
631          * many System V curses implementations don't get it right.
632          */
633         (void)tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &clp->orig);
634
635         /* Stop the process group. */
636         (void)kill(0, SIGTSTP);
637
638         /* Time passes ... */
639
640         /*
641          * If we received a killer signal, we're done.  Leave everything
642          * unchanged.  In addition, the terminal has already been reset
643          * correctly, so leave it alone.
644          */
645         if (clp->killersig) {
646                 F_CLR(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT);
647                 return (0);
648         }
649
650 #ifdef HAVE_BSD_CURSES
651         /* Restore terminal settings. */
652         if (F_ISSET(clp, CL_STDIN_TTY))
653                 (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t);
654
655         (void)cl_attr(sp, SA_ALTERNATE, 1);
656 #endif
657
658         /* Set the window name. */
659         (void)cl_rename(sp, sp->frp->name, 1);
660
661         /* Put the cursor keys into application mode. */
662         (void)keypad(stdscr, TRUE);
663
664         /* Refresh and repaint the screen. */
665         (void)move(oldy, oldx);
666         (void)cl_refresh(sp, 1);
667
668         /* If the screen changed size, set the SIGWINCH bit. */
669         if (cl_ssize(sp, 1, NULL, NULL, &changed))
670                 return (1);
671         if (changed)
672                 F_SET(CLP(sp), CL_SIGWINCH);
673
674         return (0);
675 }
676
677 /*
678  * cl_usage --
679  *      Print out the curses usage messages.
680  * 
681  * PUBLIC: void cl_usage __P((void));
682  */
683 void
684 cl_usage()
685 {
686 #define USAGE "\
687 usage: ex [-eFRrSsv] [-c command] [-t tag] [-w size] [file ...]\n\
688 usage: vi [-eFlRrSv] [-c command] [-t tag] [-w size] [file ...]\n"
689         (void)fprintf(stderr, "%s", USAGE);
690 #undef  USAGE
691 }
692
693 #ifdef DEBUG
694 /*
695  * gdbrefresh --
696  *      Stub routine so can flush out curses screen changes using gdb.
697  */
698 int
699 gdbrefresh()
700 {
701         refresh();
702         return (0);             /* XXX Convince gdb to run it. */
703 }
704 #endif