3 * Keith Bostic. All rights reserved.
5 * See the LICENSE file for redistribution information.
11 static const char sccsid
[] = "@(#)ip_cl.c 8.4 (Berkeley) 10/13/96";
14 #include <sys/types.h>
15 #include <sys/ioctl.h>
16 #include <sys/queue.h>
17 #include <sys/select.h>
19 #include <bitstring.h>
30 #include "../common/common.h"
32 #include "pathnames.h"
34 size_t cols
, rows
; /* Screen columns, rows. */
35 int die
; /* Child died. */
36 int i_fd
, o_fd
; /* Input/output fd's. */
37 int resize
; /* Window resized. */
39 void arg_format
__P((int *, char **[], int, int));
40 void attach
__P((void));
41 void ip_cur_end
__P((void));
42 void ip_cur_init
__P((void));
43 void ip_read
__P((void));
44 void ip_resize
__P((void));
45 int ip_send
__P((char *, IP_BUF
*));
46 void ip_siginit
__P((void));
47 int ip_trans
__P((char *, size_t, size_t *));
48 void nomem
__P((void));
49 void onchld
__P((int));
50 void onintr
__P((int));
51 void onwinch
__P((int));
52 void trace
__P((const char *, ...));
53 void usage
__P((void));
62 size_t blen
, len
, skip
;
63 int ch
, nr
, rpipe
[2], wpipe
[2];
66 while ((ch
= getopt(argc
, argv
, "D")) != EOF
)
79 * Open the communications pipes. The pipes are named from our
80 * viewpoint, so we read from rpipe[0] and write to wpipe[1].
81 * Vi reads from wpipe[0], and writes to rpipe[1].
83 if (pipe(rpipe
) == -1 || pipe(wpipe
) == -1) {
84 perror("ip_cl: pipe");
91 * Format our arguments, adding a -I to the list. The first file
92 * descriptor to the -I argument is vi's input, and the second is
95 arg_format(&argc
, &argv
, wpipe
[0], rpipe
[1]);
98 switch (pid
= fork()) {
100 perror("ip_cl: fork");
104 perror("ip_cl: execv ../build/nvi");
106 default: /* Ip_cl. */
111 * Allocate initial input buffer.
113 * We don't dynamically resize, so there better not be any individual
114 * messages larger than this buffer.
117 if ((bp
= malloc(blen
)) == NULL
)
120 /* Clear the file descriptor mask. */
123 /* Initialize signals. */
126 /* Initialize the curses screen. */
129 /* The first thing vi wants is the screen size. */
138 * Race #1: if there's an event coming from vi that requires
139 * that we know what size the screen is, and we take a resize
140 * signal, we'll differ from vi in the size of the screen for
141 * that event. Fixing this will requires information attached
142 * to message as to what set of state was in place when the
143 * message was sent. Not hard, but not worth doing now.
145 * Race #2: we cycle, handling resize events until there aren't
146 * any waiting. We then do a select. If the resize signal
147 * arrives after we exit the loop but before we enter select,
148 * we'll wait on the user to enter a keystroke, handle it and
149 * then handle the resize.
158 /* Wait until vi or the screen wants to talk. */
159 FD_SET(i_fd
, &fdset
);
160 FD_SET(STDIN_FILENO
, &fdset
);
162 switch (select(i_fd
+ 1, &fdset
, NULL
, NULL
, NULL
)) {
164 abort(); /* Timeout. */
169 perror("ip_cl: select");
175 /* Read waiting tty characters and send them to vi. */
176 if (FD_ISSET(STDIN_FILENO
, &fdset
)) {
181 /* Read waiting vi messages and translate to curses calls. */
182 switch (nr
= read(i_fd
, bp
+ len
, blen
- len
)) {
186 perror("ip_cl: read");
192 /* Parse to data end or partial message. */
193 for (len
+= nr
, skip
= 0; len
> skip
&&
194 ip_trans(bp
+ skip
, len
- skip
, &skip
) == 1;);
196 /* Copy any partial messages down in the buffer. */
199 memmove(bp
, bp
+ skip
, len
);
202 /* End the screen. */
210 * Read characters from the screen and send them to vi.
219 /* Read waiting tty characters. */
220 switch (nr
= read(STDIN_FILENO
, bp
, sizeof(bp
))) {
224 perror("ip_cl: read");
230 ipb
.code
= IPO_STRING
;
238 * Translate vi messages into curses calls.
241 ip_trans(bp
, len
, skipp
)
246 size_t cno
, lno
, nlen
, oldy
, oldx
, spcnt
;
270 p
= bp
+ IPO_CODE_LEN
;
271 for (; *fmt
!= '\0'; ++fmt
)
277 memcpy(&ipb
.val1
, p
, IPO_INT_LEN
);
278 ipb
.val1
= ntohl(ipb
.val1
);
285 memcpy(&ipb
.val2
, p
, IPO_INT_LEN
);
286 ipb
.val2
= ntohl(ipb
.val2
);
293 memcpy(&ipb
.len
, p
, IPO_INT_LEN
);
294 ipb
.len
= ntohl(ipb
.len
);
308 trace("addnstr {%.*s}\n", (int)ipb
.len
, ipb
.str
);
310 (void)addnstr(ipb
.str
, ipb
.len
);
316 trace("attr: alternate\n");
325 trace("attr: inverse\n");
341 (void)write(1, "\007", 1); /* '\a' */
345 trace("busy {%.*s}\n", (int)ipb
.len
, ipb
.str
);
350 * ip_busy(ipb.str, ipb.len);
373 trace("move: %lu %lu\n", (u_long
)ipb
.val1
, (u_long
)ipb
.val2
);
375 (void)move(ipb
.val1
, ipb
.val2
);
392 trace("rename {%.*s}\n", (int)ipb
.len
, ipb
.str
);
397 * ip_rename(ipb.str, ipb.len);
402 trace("rewrite {%lu}\n", (u_long
)ipb
.val1
);
404 getyx(stdscr
, oldy
, oldx
);
405 for (lno
= ipb
.val1
, cno
= spcnt
= 0;;) {
406 (void)move(lno
, cno
);
411 (void)move(lno
, cno
- spcnt
);
412 for (; spcnt
> 0; --spcnt
)
419 (void)move(oldy
, oldx
);
423 * XXX: Protocol is out of sync?
435 arg_format(argcp
, argvp
, i_fd
, o_fd
)
436 int *argcp
, i_fd
, o_fd
;
439 char **largv
, *iarg
, *p
;
441 /* Get space for the argument array and the -I argument. */
442 if ((iarg
= malloc(64)) == NULL
||
443 (largv
= malloc((*argcp
+ 3) * sizeof(char *))) == NULL
) {
447 memcpy(largv
+ 2, *argvp
, *argcp
* sizeof(char *) + 1);
449 /* Reset argv[0] to be the exec'd program. */
450 if ((p
= strrchr(VI
, '/')) == NULL
)
455 /* Create the -I argument. */
456 (void)sprintf(iarg
, "-I%d%s%d", i_fd
, ".", o_fd
);
459 /* Reset the argument array. */
465 * Initialize the curses screen.
472 * This is 4BSD curses' specific -- if this is to be a real program
473 * we'll have to do all the stuff that we do in the cl directory to
474 * run with different curses variants.
476 if (initscr() == ERR
) {
477 perror("ip_cl: initscr");
488 * End the curses screen.
495 (void)move(rows
- 1, 0);
502 * Initialize the signals.
507 /* We need to know if vi dies horribly. */
508 (void)signal(SIGCHLD
, onchld
);
510 /* We want to allow interruption at least for now. */
511 (void)signal(SIGINT
, onintr
);
514 /* We need to know if the screen is resized. */
515 (void)signal(SIGWINCH
, onwinch
);
521 * Send the window size.
529 if (ioctl(STDERR_FILENO
, TIOCGWINSZ
, &win
) == -1) {
530 perror("ip_cl: TIOCGWINSZ");
534 if (rows
== win
.ws_row
&& cols
== win
.ws_col
)
537 ipb
.val1
= rows
= win
.ws_row
;
538 ipb
.val2
= cols
= win
.ws_col
;
539 ipb
.code
= IPO_RESIZE
;
545 * Construct and send an IP buffer.
559 if (blen
== 0 && (bp
= malloc(blen
= 512)) == NULL
)
565 nlen
+= IPO_CODE_LEN
;
568 for (; *fmt
!= '\0'; ++fmt
)
570 case '1': /* Value 1. */
571 ilen
= htonl(ipbp
->val1
);
573 case '2': /* Value 2. */
574 ilen
= htonl(ipbp
->val2
);
575 value
: nlen
+= IPO_INT_LEN
;
577 blen
= blen
* 2 + nlen
;
579 if ((bp
= realloc(bp
, blen
)) == NULL
)
583 memmove(p
, &ilen
, IPO_INT_LEN
);
586 case 's': /* String. */
587 ilen
= ipbp
->len
; /* XXX: conversion. */
589 nlen
+= IPO_INT_LEN
+ ipbp
->len
;
591 blen
= blen
* 2 + nlen
;
593 if ((bp
= realloc(bp
, blen
)) == NULL
)
597 memmove(p
, &ilen
, IPO_INT_LEN
);
599 memmove(p
, ipbp
->str
, ipbp
->len
);
605 for (n
= p
- bp
, p
= bp
; n
> 0; --n
, ++p
)
607 (void)trace("%c", *p
);
609 trace("<%x>", (u_char
)*p
);
613 for (n
= p
- bp
, p
= bp
; n
> 0; n
-= nw
, p
+= nw
)
614 if ((nw
= write(o_fd
, p
, n
)) < 0) {
615 perror("ip_cl: write");
643 /* Interrupt select if it's running. */
644 (void)kill(getpid(), SIGINT
);
656 * If we receive an interrupt, we may have sent it ourselves.
657 * If not, die from the signal.
661 (void)signal(SIGINT
, SIG_DFL
);
662 kill(getpid(), SIGINT
);
682 (void)printf("process %lu waiting, enter <CR> to continue: ",
684 (void)fflush(stdout
);
686 if ((fd
= open(_PATH_TTY
, O_RDONLY
, 0)) < 0) {
691 if (read(fd
, &ch
, 1) != 1) {
695 } while (ch
!= '\n' && ch
!= '\r');
708 * debugging trace routine.
712 trace(const char *fmt
, ...)
722 if (tfp
== NULL
&& (tfp
= fopen(TR
, "w")) == NULL
)
730 (void)vfprintf(tfp
, fmt
, ap
);
740 (void)fprintf(stderr
, "usage: ip_cl [-D]\n");