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