Merge from vendor branch DIFFUTILS:
[dragonfly.git] / contrib / nvi / ip_cl / ip_cl.c
1 /*-
2  * Copyright (c) 1996
3  *      Keith Bostic.  All rights reserved.
4  *
5  * See the LICENSE file for redistribution information.
6  */
7
8 #include "config.h"
9
10 #ifndef lint
11 static const char sccsid[] = "@(#)ip_cl.c       8.4 (Berkeley) 10/13/96";
12 #endif /* not lint */
13
14 #include <sys/types.h>
15 #include <sys/ioctl.h>
16 #include <sys/queue.h>
17 #include <sys/select.h>
18
19 #include <bitstring.h>
20 #include <ctype.h>
21 #include <curses.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <signal.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29
30 #include "../common/common.h"
31 #include "../ip/ip.h"
32 #include "pathnames.h"
33
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. */
38
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));
54
55 int
56 main(argc, argv)
57         int argc;
58         char *argv[];
59 {
60         fd_set fdset;
61         pid_t pid;
62         size_t blen, len, skip;
63         int ch, nr, rpipe[2], wpipe[2];
64         char *bp;
65
66         while ((ch = getopt(argc, argv, "D")) != EOF)
67                 switch (ch) {
68                 case 'D':
69                         attach();
70                         break;
71                 case '?':
72                 default:
73                         usage();
74                 }
75         argc -= optind;
76         argv += optind;
77
78         /*
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].
82          */
83         if (pipe(rpipe) == -1 || pipe(wpipe) == -1) {
84                 perror("ip_cl: pipe");
85                 exit (1);
86         }
87         i_fd = rpipe[0];
88         o_fd = wpipe[1];
89
90         /*
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
93          * vi's output.
94          */
95         arg_format(&argc, &argv, wpipe[0], rpipe[1]);
96
97         /* Run vi. */
98         switch (pid = fork()) {
99         case -1:                                /* Error. */
100                 perror("ip_cl: fork");
101                 exit (1);
102         case 0:                                 /* Vi. */
103                 execv(VI, argv);
104                 perror("ip_cl: execv ../build/nvi");
105                 exit (1);
106         default:                                /* Ip_cl. */
107                 break;
108         }
109
110         /*
111          * Allocate initial input buffer.
112          * XXX
113          * We don't dynamically resize, so there better not be any individual
114          * messages larger than this buffer.
115          */
116         blen = 4 * 1024;
117         if ((bp = malloc(blen)) == NULL)
118                 nomem();
119
120         /* Clear the file descriptor mask. */
121         FD_ZERO(&fdset);
122
123         /* Initialize signals. */
124         ip_siginit();
125
126         /* Initialize the curses screen. */
127         ip_cur_init();
128
129         /* The first thing vi wants is the screen size. */
130         ip_resize();
131
132         /* Main loop. */
133         for (len = 0;;) {
134                 if (die)
135                         break;
136                 /*
137                  * XXX
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.
144                  *
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.
150                  */
151                 while (resize) {
152                         resize = 0;
153                         ip_resize();
154                         ip_cur_end();
155                         ip_cur_init();
156                 }
157
158                 /* Wait until vi or the screen wants to talk. */
159                 FD_SET(i_fd, &fdset);
160                 FD_SET(STDIN_FILENO, &fdset);
161                 errno = 0;
162                 switch (select(i_fd + 1, &fdset, NULL, NULL, NULL)) {
163                 case 0:
164                         abort();                /* Timeout. */
165                         /* NOTREACHED */
166                 case -1:
167                         if (errno == EINTR)
168                                 continue;
169                         perror("ip_cl: select");
170                         exit (1);
171                 default:
172                         break;
173                 }
174
175                 /* Read waiting tty characters and send them to vi. */
176                 if (FD_ISSET(STDIN_FILENO, &fdset)) {
177                         ip_read();
178                         continue;
179                 }
180
181                 /* Read waiting vi messages and translate to curses calls. */
182                 switch (nr = read(i_fd, bp + len, blen - len)) {
183                 case 0:
184                         continue;
185                 case -1:
186                         perror("ip_cl: read");
187                         exit (1);
188                 default:
189                         break;
190                 }
191
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;);
195
196                 /* Copy any partial messages down in the buffer. */
197                 len -= skip;
198                 if (len > 0)
199                         memmove(bp, bp + skip, len);
200         }
201
202         /* End the screen. */
203         ip_cur_end();
204
205         exit (0);
206 }
207
208 /*
209  * ip_read --
210  *      Read characters from the screen and send them to vi.
211  */
212 void
213 ip_read()
214 {
215         IP_BUF ipb;
216         int nr;
217         char bp[1024];
218
219         /* Read waiting tty characters. */
220         switch (nr = read(STDIN_FILENO, bp, sizeof(bp))) {
221         case 0:
222                 return;
223         case -1:
224                 perror("ip_cl: read");
225                 exit (1);
226         default:
227                 break;
228         }
229
230         ipb.code = IPO_STRING;
231         ipb.len = nr;
232         ipb.str = bp;
233         ip_send("s", &ipb);
234 }
235
236 /*
237  * ip_trans --
238  *      Translate vi messages into curses calls.
239  */
240 int
241 ip_trans(bp, len, skipp)
242         char *bp;
243         size_t len, *skipp;
244 {
245         IP_BUF ipb;
246         size_t cno, lno, nlen, oldy, oldx, spcnt;
247         int ch;
248         char *fmt, *p;
249
250         switch (bp[0]) {
251         case IPO_ADDSTR:
252         case IPO_RENAME:
253                 fmt = "s";
254                 break;
255         case IPO_BUSY:
256                 fmt = "s1";
257                 break;
258         case IPO_ATTRIBUTE:
259         case IPO_MOVE:
260                 fmt = "12";
261                 break;
262         case IPO_REWRITE:
263                 fmt = "1";
264                 break;
265         default:
266                 fmt = "";
267         }
268
269         nlen = IPO_CODE_LEN;
270         p = bp + IPO_CODE_LEN;
271         for (; *fmt != '\0'; ++fmt)
272                 switch (*fmt) {
273                 case '1':
274                         nlen += IPO_INT_LEN;
275                         if (len < nlen)
276                                 return (0);
277                         memcpy(&ipb.val1, p, IPO_INT_LEN);
278                         ipb.val1 = ntohl(ipb.val1);
279                         p += IPO_INT_LEN;
280                         break;
281                 case '2':
282                         nlen += IPO_INT_LEN;
283                         if (len < nlen)
284                                 return (0);
285                         memcpy(&ipb.val2, p, IPO_INT_LEN);
286                         ipb.val2 = ntohl(ipb.val2);
287                         p += IPO_INT_LEN;
288                         break;
289                 case 's':
290                         nlen += IPO_INT_LEN;
291                         if (len < nlen)
292                                 return (0);
293                         memcpy(&ipb.len, p, IPO_INT_LEN);
294                         ipb.len = ntohl(ipb.len);
295                         p += IPO_INT_LEN;
296                         nlen += ipb.len;
297                         if (len < nlen)
298                                 return (0);
299                         ipb.str = p;
300                         p += ipb.len;
301                         break;
302                 }
303         *skipp += nlen;
304
305         switch (bp[0]) {
306         case IPO_ADDSTR:
307 #ifdef TR
308                 trace("addnstr {%.*s}\n", (int)ipb.len, ipb.str);
309 #endif
310                 (void)addnstr(ipb.str, ipb.len);
311                 break;
312         case IPO_ATTRIBUTE:
313                 switch (ipb.val1) {
314                 case SA_ALTERNATE:
315 #ifdef TR
316                         trace("attr: alternate\n");
317 #endif
318                         /*
319                          * XXX
320                          * Nothing.
321                          */
322                         break;
323                 case SA_INVERSE:
324 #ifdef TR
325                         trace("attr: inverse\n");
326 #endif
327                         if (ipb.val2)
328                                 (void)standout();
329                         else
330                                 (void)standend();
331                         break;
332                 default:
333                         abort();
334                         /* NOTREACHED */
335                 }
336                 break;
337         case IPO_BELL:
338 #ifdef TR
339                 trace("bell\n");
340 #endif
341                 (void)write(1, "\007", 1);              /* '\a' */
342                 break;
343         case IPO_BUSY:
344 #ifdef TR
345                 trace("busy {%.*s}\n", (int)ipb.len, ipb.str);
346 #endif
347                 /*
348                  * XXX
349                  * Nothing...
350                  * ip_busy(ipb.str, ipb.len);
351                  */
352                 break;
353         case IPO_CLRTOEOL:
354 #ifdef TR
355                 trace("clrtoeol\n");
356 #endif
357                 clrtoeol();
358                 break;
359         case IPO_DELETELN:
360 #ifdef TR
361                 trace("deleteln\n");
362 #endif
363                 deleteln();
364                 break;
365         case IPO_INSERTLN:
366 #ifdef TR
367                 trace("insertln\n");
368 #endif
369                 insertln();
370                 break;
371         case IPO_MOVE:
372 #ifdef TR
373                 trace("move: %lu %lu\n", (u_long)ipb.val1, (u_long)ipb.val2);
374 #endif
375                 (void)move(ipb.val1, ipb.val2);
376                 break;
377         case IPO_REDRAW:
378 #ifdef TR
379                 trace("redraw\n");
380 #endif
381                 clearok(curscr, 1);
382                 refresh();
383                 break;
384         case IPO_REFRESH:
385 #ifdef TR
386                 trace("refresh\n");
387 #endif
388                 refresh();
389                 break;
390         case IPO_RENAME:
391 #ifdef TR
392                 trace("rename {%.*s}\n", (int)ipb.len, ipb.str);
393 #endif
394                 /*
395                  * XXX
396                  * Nothing...
397                  * ip_rename(ipb.str, ipb.len);
398                  */
399                 break;
400         case IPO_REWRITE:
401 #ifdef TR
402                 trace("rewrite {%lu}\n", (u_long)ipb.val1);
403 #endif
404                 getyx(stdscr, oldy, oldx);
405                 for (lno = ipb.val1, cno = spcnt = 0;;) {
406                         (void)move(lno, cno);
407                         ch = winch(stdscr);
408                         if (isblank(ch))
409                                 ++spcnt;
410                         else {
411                                 (void)move(lno, cno - spcnt);
412                                 for (; spcnt > 0; --spcnt)
413                                         (void)addch(' ');
414                                 (void)addch(ch);
415                         }
416                         if (++cno >= cols)
417                                 break;
418                 }
419                 (void)move(oldy, oldx);
420                 break;
421         default:
422                 /*
423                  * XXX: Protocol is out of sync?  
424                  */
425                 abort();
426         }
427
428         return (1);
429 }
430
431 /*
432  * arg_format
433  */
434 void
435 arg_format(argcp, argvp, i_fd, o_fd)
436         int *argcp, i_fd, o_fd;
437         char **argvp[];
438 {
439         char **largv, *iarg, *p;
440
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) {
444                 perror("ip_cl");
445                 exit (1);
446         }
447         memcpy(largv + 2, *argvp, *argcp * sizeof(char *) + 1);
448
449         /* Reset argv[0] to be the exec'd program. */
450         if ((p = strrchr(VI, '/')) == NULL)
451                 largv[0] = VI;
452         else
453                 largv[0] = p + 1;
454
455         /* Create the -I argument. */
456         (void)sprintf(iarg, "-I%d%s%d", i_fd, ".", o_fd);
457         largv[1] = iarg;
458
459         /* Reset the argument array. */
460         *argvp = largv;
461 }
462
463 /*
464  * ip_cur_init --
465  *      Initialize the curses screen.
466  */
467 void
468 ip_cur_init()
469 {
470         /* 
471          * XXX
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.
475          */
476         if (initscr() == ERR) {
477                 perror("ip_cl: initscr");
478                 exit (1);
479         }
480         noecho();
481         nonl();
482         raw();
483         idlok(stdscr, 1);
484 }
485
486 /*
487  * ip_cur_end --
488  *      End the curses screen.
489  */
490 void
491 ip_cur_end()
492 {
493         (void)move(0, 0);
494         (void)deleteln();
495         (void)move(rows - 1, 0);
496         (void)refresh();
497         (void)endwin();
498 }
499
500 /*
501  * ip_siginit --
502  *      Initialize the signals.
503  */
504 void
505 ip_siginit()
506 {
507         /* We need to know if vi dies horribly. */
508         (void)signal(SIGCHLD, onchld);
509
510         /* We want to allow interruption at least for now. */
511         (void)signal(SIGINT, onintr);
512
513 #ifdef SIGWINCH
514         /* We need to know if the screen is resized. */
515         (void)signal(SIGWINCH, onwinch);
516 #endif
517 }
518
519 /*
520  * ip_resize --
521  *      Send the window size.
522  */
523 void
524 ip_resize()
525 {
526         struct winsize win;
527         IP_BUF ipb;
528
529         if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) == -1) {
530                 perror("ip_cl: TIOCGWINSZ");
531                 exit(1);
532         }
533
534         if (rows == win.ws_row && cols == win.ws_col)
535                 return;
536
537         ipb.val1 = rows = win.ws_row;
538         ipb.val2 = cols = win.ws_col;
539         ipb.code = IPO_RESIZE;
540         ip_send("12", &ipb);
541 }
542
543 /*
544  * ip_send --
545  *      Construct and send an IP buffer.
546  */
547 int
548 ip_send(fmt, ipbp)
549         char *fmt;
550         IP_BUF *ipbp;
551 {
552         static char *bp;
553         static size_t blen;
554         size_t off;
555         u_int32_t ilen;
556         int nlen, n, nw;
557         char *p;
558         
559         if (blen == 0 && (bp = malloc(blen = 512)) == NULL)
560                 nomem();
561
562         p = bp;
563         nlen = 0;
564         *p++ = ipbp->code;
565         nlen += IPO_CODE_LEN;
566
567         if (fmt != NULL)
568                 for (; *fmt != '\0'; ++fmt)
569                         switch (*fmt) {
570                         case '1':                       /* Value 1. */
571                                 ilen = htonl(ipbp->val1);
572                                 goto value;
573                         case '2':                       /* Value 2. */
574                                 ilen = htonl(ipbp->val2);
575 value:                          nlen += IPO_INT_LEN;
576                                 if (nlen >= blen) {
577                                         blen = blen * 2 + nlen;
578                                         off = p - bp;
579                                         if ((bp = realloc(bp, blen)) == NULL)
580                                                 nomem();
581                                         p = bp + off;
582                                 }
583                                 memmove(p, &ilen, IPO_INT_LEN);
584                                 p += IPO_INT_LEN;
585                                 break;
586                         case 's':                       /* String. */
587                                 ilen = ipbp->len;       /* XXX: conversion. */
588                                 ilen = htonl(ilen);
589                                 nlen += IPO_INT_LEN + ipbp->len;
590                                 if (nlen >= blen) {
591                                         blen = blen * 2 + nlen;
592                                         off = p - bp;
593                                         if ((bp = realloc(bp, blen)) == NULL)
594                                                 nomem();
595                                         p = bp + off;
596                                 }
597                                 memmove(p, &ilen, IPO_INT_LEN);
598                                 p += IPO_INT_LEN;
599                                 memmove(p, ipbp->str, ipbp->len);
600                                 p += ipbp->len;
601                                 break;
602                         }
603 #ifdef TR
604         trace("WROTE: ");
605         for (n = p - bp, p = bp; n > 0; --n, ++p)
606                 if (isprint(*p))
607                         (void)trace("%c", *p);
608                 else
609                         trace("<%x>", (u_char)*p);
610         trace("\n");
611 #endif
612
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");
616                         exit(1);
617                 }
618
619         return (0);
620 }
621
622 void
623 nomem()
624 {
625         perror("ip_cl");
626         exit (1);
627 }
628
629 /*
630  * onchld --
631  *      Handle SIGCHLD.
632  */
633 void
634 onchld(signo)
635         int signo;
636 {
637         die = 1;
638
639 #ifdef TR
640         trace("SIGCHLD\n");
641 #endif
642
643         /* Interrupt select if it's running. */
644         (void)kill(getpid(), SIGINT);
645 }
646
647 /*
648  * onintr --
649  *      Handle SIGINT.
650  */
651 void
652 onintr(signo)
653         int signo;
654 {
655         /*
656          * If we receive an interrupt, we may have sent it ourselves.
657          * If not, die from the signal.
658          */
659         if (die)
660                 return;
661         (void)signal(SIGINT, SIG_DFL);
662         kill(getpid(), SIGINT);
663 }
664
665 /*
666  * onwinch --
667  *      Handle SIGWINCH.
668  */
669 void
670 onwinch(signo)
671         int signo;
672 {
673         resize = 1;
674 }
675
676 void
677 attach()
678 {
679         int fd;
680         char ch;
681
682         (void)printf("process %lu waiting, enter <CR> to continue: ",
683             (u_long)getpid());
684         (void)fflush(stdout);
685
686         if ((fd = open(_PATH_TTY, O_RDONLY, 0)) < 0) {
687                 perror(_PATH_TTY);
688                 exit (1);;
689         }
690         do {
691                 if (read(fd, &ch, 1) != 1) {
692                         (void)close(fd);
693                         return;
694                 }
695         } while (ch != '\n' && ch != '\r');
696         (void)close(fd);
697 }
698
699 #ifdef TR
700 #ifdef __STDC__
701 #include <stdarg.h>
702 #else
703 #include <varargs.h>
704 #endif
705
706 /*
707  * TR --
708  *      debugging trace routine.
709  */
710 void
711 #ifdef __STDC__
712 trace(const char *fmt, ...)
713 #else
714 trace(fmt, va_alist)
715         char *fmt;
716         va_dcl
717 #endif
718 {
719         static FILE *tfp;
720         va_list ap;
721
722         if (tfp == NULL && (tfp = fopen(TR, "w")) == NULL)
723                 tfp = stderr;
724         
725 #ifdef __STDC__
726         va_start(ap, fmt);
727 #else
728         va_start(ap);
729 #endif
730         (void)vfprintf(tfp, fmt, ap);
731         va_end(ap);
732
733         (void)fflush(tfp);
734 }
735 #endif
736
737 void
738 usage()
739 {
740         (void)fprintf(stderr, "usage: ip_cl [-D]\n");
741         exit(1);
742 }