1 /****************************************************************************
2  * scream::libscream.c                         Azundris <scream@azundris.com>
3  *
4  * routines for terminal emulators to connect to screen and/or scream daemons.
5  * libscream is a double-transparency layer -- it abstracts the backend
6  * (screen or a replacement, locally or ssh-tunneled) to the front-end
7  * (a terminal-emulation such as Eterm, konsole, or multi-gnome-terminal)
8  * and vice versa.  several sessions can be open at once.
9  *
10  * Lesser GNU Public Licence applies.
11  * Distributed with Eterm under the same license terms as Eterm itself.
12  * Thread-safe:  untested
13  *
14  * 2002/04/19  Azundris  incept
15  * 2002/05/04  Azundris  support for esoteric screens, thanks to Till
16  * 2002/05/12  Azundris  edit display names, send statement, tab completion
17  * 2002/05/13  Azundris  ssh tunnel through firewall
18  * 2002/05/17  Azundris  supports systemwide screenrc (thanks mej)
19  * 2002/05/18  Azundris  remote handling improved (thanks tillsan, tfing)
20  * 2002/05/21  Azundris  code restructuring, basic tab tear-off
21  * 2002/06/04  Azundris  advanced tab tear-off
22  * 2002/06/05  Azundris  basic twin support
23  * 2002/06/11  Azundris  more  twin support
24  * 2005/01/04  Azundris  send statements rather than hotkeys wherever possible
25  ***************************************************************************/
26 
27 
28 
29 #undef NS_DEBUG
30 
31 #include <stdio.h>              /* stderr, fprintf, snprintf() */
32 #include <string.h>             /* bzero() */
33 #include <pwd.h>                /* getpwuid() */
34 #include <sys/types.h>          /* getpwuid() */
35 #include <sys/stat.h>           /* stat() */
36 #include <unistd.h>             /* getuid() */
37 #include <stdlib.h>             /* atoi() */
38 #include <netdb.h>              /* getservbyname() */
39 #include <netinet/in.h>         /* ntohs() */
40 #include <limits.h>             /* PATH_MAX */
41 #include <ctype.h>              /* isspace() */
42 #include <errno.h>              /* errno */
43 #include <sys/socket.h>
44 
45 #include "config.h"
46 #include "feature.h"
47 
48 /* use libast if we have it */
49 #ifdef DEBUG_ESCREEN
50 #  include <libast.h>
51 #else
52 #  define MALLOC(a) malloc(a)
53 #  define FREE(a) free(a)
54 #  define STRDUP(a) strdup(a)
55 #  ifdef NS_DEBUG
56 #    define D_ESCREEN(a)  fprintf(stderr,a);
57 #  else
58 #    define D_ESCREEN(a)
59 #  endif
60 #endif
61 
62 #include "scream.h"             /* structs, defs, headers */
63 #include "screamcfg.h"          /* user-tunables */
64 
65 #ifdef NS_HAVE_TWIN
66 #  include <Tw/Tw.h>
67 #  include <Tw/Tw1.h>
68 #  include <Tw/Twerrno.h>
69 #endif
70 
71 #ifndef MAXPATHLEN
72 #  ifdef PATH_MAX
73 #    define MAXPATHLEN PATH_MAX
74 #  elif defined(MAX_PATHLEN)
75 #    define MAXPATHLEN MAX_PATHLEN
76 #  endif
77 #endif
78 
79 
80 
81 /***************************************************************************/
82 /* module-global vars */
83 /**********************/
84 
85 
86 
87 static long err_inhibit = 0;    /* bits. avoid telling same error twice. */
88 static _ns_sess *sa = NULL;     /* anchor for session list */
89 static _ns_hop *ha = NULL;      /* anchor for hop list */
90 
91 
92 
93 /***************************************************************************/
94 /* forward declarations */
95 /************************/
96 
97 
98 
99 static void ns_desc_hop(_ns_hop *, char *);
100 static int ns_parse_screenrc(_ns_sess *, char *, ns_esc_whence);
101 static int ns_mov_screen_disp(_ns_sess *, int, int);
102 static _ns_sess *ns_dst_sess(_ns_sess **);
103 
104 #ifdef NS_HAVE_SCREEN
105 static int ns_inp_tab(void *, char *, size_t, size_t);
106 #endif
107 
108 
109 
110 /****************************************************************************
111            _     _     _ _          _
112  _ __ ___ (_) __| | __| | | ___    | | __ _ _   _  ___ _ __
113 | '_ ` _ \| |/ _` |/ _` | |/ _ \   | |/ _` | | | |/ _ \ '__|
114 | | | | | | | (_| | (_| | |  __/   | | (_| | |_| |  __/ |
115 |_| |_| |_|_|\__,_|\__,_|_|\___|   |_|\__,_|\__, |\___|_|
116                                             |___/
117 
118 central abstraction layer
119 
120   this abstracts the front-end (terminal emulator) against the back-end
121   (local or remote terminal server), and the back-end against the front-end:
122 
123   - front-end hands us an URL we attach to (without knowing about the backend)
124   - CAL receives messages from back-end and calls the external function (efun)
125     the front-end registered for this event
126   - CAL functions are called from the front-end and send data fitting the
127     session-type to the backend
128 */
129 
130 
131 
132 /* test if we have a valid callback for function-type "e".
133   !p  a variable of the "_ns_efuns *" type.  will contain a pointer to
134       an efun struct containing a function pointer to the requested function
135       if such a struct exists, or NULL, if it doesn't exist
136    s  a variable of the "_ns_sess *" type, or NULL (see ns_get_efuns())
137    d  a variable of the "_nd_disp *" type, or NULL (see ns_get_efuns())
138    e  the name of an element of "_ns_efuns"
139   !<- conditional execution of next (compound-) statement (which would
140       normally be (p)->(e)(...), the call of the function e).
141  */
142 #define NS_EFUN_EXISTS(p,s,d,e) (((p)=ns_get_efuns((s),(d)))&&((p)->e))
143 
144 
145 
146 /***************************************************************************/
147 /* constructors/destructors */
148 /****************************/
149 
150 
151 
152 /* ns_free
153    free a string (or whatever) */
154 
155 static void
156 *
ns_free(char ** x)157 ns_free(char **x)
158 {
159     if (x && !*x) {
160         FREE(*x);
161         *x = NULL;
162     }
163     return NULL;
164 }
165 
166 
167 
168 /* ns_new_hop.  create and initialize a hop struct.
169    lp    local port.  if 0:  if otherwise matching hop exists, reuse that.
170                              otherwise, find the first FREE (as in, not used
171                              by us) port, starting with NS_MIN_PORT.
172    fw    firewall machine.  numeric or symbolic.
173    fp    foreign port. if 0: default to SSH port.
174    delay wait n seconds for tunnel to come up before trying to use it
175    s     the session to add the hop to
176    <-    a matching (existing or newly created) hop structure, or NULL */
177 
178 static _ns_hop *
ns_new_hop(int lp,char * fw,int fp,int delay,_ns_sess * s)179 ns_new_hop(int lp, char *fw, int fp, int delay, _ns_sess * s)
180 {
181     _ns_hop *h = ha;
182 
183     if (!fw || !*fw)
184         return NULL;
185 
186     if (!fp)
187         fp = ns_get_ssh_port(); /* remote port defaults to SSH */
188 
189     if (s) {
190         /* see if we already have a matching hop. */
191         while (h && !(((h->localport == lp) || (!lp)) && (!strcmp(h->fw, fw)) && (h->fwport == fp) && (h->sess->port == s->port)
192                       && (!strcmp(h->sess->host, s->host))))
193             h = h->next;
194 
195         if (h) {
196             if (delay)
197                 h->delay = delay;       /* may change delay! */
198             h->refcount++;
199             return h;
200         }
201     }
202 
203     h = MALLOC(sizeof(_ns_hop));
204     if (h) {
205         bzero(h, sizeof(_ns_hop));
206         if ((h->fw = STRDUP(fw))) {
207             if (!lp) {
208                 int tmp_sock;
209 
210                 tmp_sock = socket(PF_INET, SOCK_STREAM, 6);
211                 if (tmp_sock > 0) {
212                     struct sockaddr_in addr;
213 
214                     addr.sin_family = AF_INET;
215                     addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
216                     for (lp = NS_MIN_PORT; (lp > 0) && (lp < NS_MAX_PORT); lp++) {
217                         addr.sin_port = htons(lp);
218 
219                         if (bind(tmp_sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_in))) {
220                             D_ESCREEN(("Unable to bind socket %d to 127.0.0.1:%hd -- %s\n", tmp_sock, lp, strerror(errno)));
221                         } else if (listen(tmp_sock, 1)) {
222                             D_ESCREEN(("Unable to listen on port %hd -- %s\n", lp, strerror(errno)));
223                         } else {
224                             /* We can listen on this port.  Use it! */
225                             /* FIXME:  Minor race condition between port selection and ssh call. */
226                             D_ESCREEN(("Got available listening port %d.\n", lp));
227                             break;
228                         }
229                     }
230                     if ((lp < 0) || (lp == NS_MAX_PORT)) {
231                         /* We're going to fail anyway, so just throw something in. */
232                         lp = NS_MIN_PORT + (random() % (NS_MAX_PORT - NS_MIN_PORT));
233                         BOUND(lp, NS_MIN_PORT, NS_MAX_PORT);
234                         D_ESCREEN(("Chose random listening port %d.\n", lp));
235                     }
236                     close(tmp_sock);
237                 }
238             }
239             h->delay = (delay ? delay : NS_TUNNEL_DELAY);
240             h->localport = lp;
241             h->fwport = fp;
242             h->refcount++;
243             h->next = ha;
244             h->sess = s;
245             ha = h;
246         } else {
247             FREE(h);
248             return NULL;
249         }
250     }
251 
252     return h;
253 }
254 
255 
256 
257 /* ns_dst_hop.  deref (and, where necessary, release) a hop struct.
258    if sp is provided, additional integrity magic will take place.
259    ss  hop to deref/FREE
260    sp  session that the hop used to belong to (NULL for none (as if))
261    <-  NULL */
262 
263 static _ns_hop *
ns_dst_hop(_ns_hop ** ss,_ns_sess * sp)264 ns_dst_hop(_ns_hop ** ss, _ns_sess * sp)
265 {
266     if (ss && *ss) {
267         _ns_hop *s = *ss;
268 
269         if (s->refcount <= 0) {
270             D_ESCREEN(("ns_dst_hop: leak alert -- trying to double-FREE hop...\n"));
271             return NULL;
272         }
273 
274         if (!--(s->refcount)) { /* was last ref to hop => FREE hop */
275             if (s->fw)
276                 FREE(s->fw);
277             if (ha == s)        /* delist */
278                 ha = s->next;
279             else {
280                 _ns_hop *h = ha;
281 
282                 while (h && h->next != s)
283                     h = h->next;
284                 if (h)
285                     h->next = s->next;
286             }
287             FREE(s);
288         } else if (sp && sp->hop == s) {
289             /* hop shouldn't point back at a session that just dereffed it
290                as it's probably about to die. fix the back ref to a session
291                that's actually valid. */
292             _ns_sess *p = sa;
293 
294             while (p && ((p == sp) || (p->port != sp->port) || (strcmp(p->host, sp->host))))
295                 p = p->next;
296             if (!p)
297                 ns_desc_hop(s,
298                             NS_PREFIX
299                             "ns_dst_sess: Leak alert -- found a hop that is only\n referenced once, but has a refcount > 1. Hop data follow");
300             else
301                 s->sess = p;
302         }
303         *ss = NULL;
304     }
305     return NULL;
306 }
307 
308 
309 
310 _ns_efuns *
ns_new_efuns(void)311 ns_new_efuns(void)
312 {
313     _ns_efuns *s = MALLOC(sizeof(_ns_efuns));
314 
315     if (s) {
316         bzero(s, sizeof(_ns_efuns));
317     }
318     return s;
319 }
320 
321 static _ns_efuns *
ns_ref_efuns(_ns_efuns ** ss)322 ns_ref_efuns(_ns_efuns ** ss)
323 {
324     if (ss && *ss) {
325         (*ss)->refcount++;
326         return *ss;
327     }
328     return NULL;
329 }
330 
331 _ns_efuns *
ns_dst_efuns(_ns_efuns ** ss)332 ns_dst_efuns(_ns_efuns ** ss)
333 {
334     if (ss && *ss) {
335         _ns_efuns *s = *ss;
336 
337         *ss = NULL;
338         if (!--(s->refcount)) {
339             FREE(s);
340         }
341     }
342     return NULL;
343 }
344 
345 
346 
347 static _ns_disp *
ns_new_disp(void)348 ns_new_disp(void)
349 {
350     _ns_disp *s = MALLOC(sizeof(_ns_disp));
351 
352     if (s) {
353         bzero(s, sizeof(_ns_disp));
354     }
355     return s;
356 }
357 
358 static _ns_disp *
ns_dst_disp(_ns_disp ** ss)359 ns_dst_disp(_ns_disp ** ss)
360 {
361     if (ss && *ss) {
362         _ns_disp *s = *ss;
363 
364         if (s->name)
365             FREE(s->name);
366         if (s->efuns)
367             ns_dst_efuns(&(s->efuns));
368         if (s->child)           /* nested screen? */
369             ns_dst_sess(&(s->child));
370         *ss = NULL;
371         FREE(s);
372     }
373     return NULL;
374 }
375 
376 static _ns_disp *
ns_dst_dsps(_ns_disp ** ss)377 ns_dst_dsps(_ns_disp ** ss)
378 {
379     if (ss && *ss) {
380         _ns_disp *s = *ss, *t;
381 
382         *ss = NULL;
383         do {
384             t = s->next;
385             ns_dst_disp(&s);
386             s = t;
387         } while (s);
388     }
389     return NULL;
390 }
391 
392 
393 
394 _ns_sess *
ns_1st_sess(void)395 ns_1st_sess(void)
396 {
397     return sa;
398 }
399 
400 
401 
402 static _ns_sess *
ns_new_sess(void)403 ns_new_sess(void)
404 {
405     _ns_sess *s = MALLOC(sizeof(_ns_sess));
406 
407     if (s) {
408         bzero(s, sizeof(_ns_sess));
409         s->escape = NS_SCREEN_ESCAPE;   /* default setup for the screen program */
410         s->literal = NS_SCREEN_LITERAL; /* set even ifndef NS_HAVE_SCREEN */
411         s->dsbb = NS_SCREEN_DEFSBB;
412         s->delay = NS_INIT_DELAY;
413         s->fd = -1;
414         s->disp = -1;
415         s->port = -1;
416         if (sa) {               /* add to end of list */
417             _ns_sess *r = sa;
418 
419             while (r->next)
420                 r = r->next;
421             r->next = s;
422         } else
423             sa = s;
424     }
425     return s;
426 }
427 
428 static _ns_sess *
ns_dst_sess(_ns_sess ** ss)429 ns_dst_sess(_ns_sess ** ss)
430 {
431     if (ss && *ss) {
432         _ns_sess *s = *ss;
433 
434         ns_dst_dsps(&(s->dsps));
435         if (s->hop)
436             ns_dst_hop(&(s->hop), s);
437         if (s->host)
438             FREE(s->host);
439         if (s->user)
440             FREE(s->user);
441         if (s->pass)
442             FREE(s->pass);
443 #ifdef NS_HAVE_TWIN
444         if (s->twin_str)
445             FREE(s->twin_str);
446 #endif
447         if (s->efuns)
448             ns_dst_efuns(&(s->efuns));
449         if (s->prvs)
450             s->prvs->next = s->next;
451         else
452             sa = s->next;       /* align anchor */
453         if (s->next)
454             s->next->prvs = s->prvs;
455         *ss = NULL;
456         FREE(s);
457     }
458     return NULL;
459 }
460 
461 
462 
463 
464 /***************************************************************************/
465 /* display-list handling */
466 /*************************/
467 
468 
469 
470 /* ns_magic_disp
471    if we pass one or both of session and display info to this, both
472    elements will be set up with sensible data afterwards.  shouldn't
473    fail unless the lists are major corrupted or no info is supplied.
474    s  a session pointer's address
475    d  a display pointer's address
476    <- NS_SUCC if we could set up the info */
477 
478 int
ns_magic_disp(_ns_sess ** s,_ns_disp ** d)479 ns_magic_disp(_ns_sess ** s, _ns_disp ** d)
480 {
481     if (!d)
482         return NS_FAIL;
483 
484     if (*d) {
485         (*d)->sess->curr = *d;
486         if (s && !*s)
487             (*s) = (*d)->sess;
488 #ifdef NS_PARANOID
489         else if (s && *s != (*d)->sess) {
490             D_ESCREEN(("ns_magic_disp: was given a disp and a session that do not belong (\n"));
491             return NS_FAIL;
492         }
493 #endif
494         return NS_SUCC;
495     } else if (s && *s) {
496         if (!(*s)->curr) {
497             if (((*s)->curr = (*s)->dsps))
498                 return NS_SUCC;
499         } else
500             return NS_SUCC;
501     }
502     return NS_FAIL;
503 }
504 
505 
506 
507 /* we need a certain display struct (index n in session s).
508    give it to us if it exists.
509    s  the session the display should be in
510    n  the index of the display (>=0).  displays in a session are sorted
511       by index, but may be sparse (0, 1, 3, 7)
512    <- the requested display */
513 
514 static _ns_disp *
disp_fetch(_ns_sess * s,int n)515 disp_fetch(_ns_sess * s, int n)
516 {
517     _ns_disp *e = NULL, *c;
518 
519     for (c = s->dsps; c && (c->index < n); c = c->next)
520         e = c;
521     if (c && (c->index == n))   /* found it */
522         return c;
523     return NULL;
524 }
525 
526 
527 
528 /* we need a certain display struct (index n in session s).
529    give it to us.  if you can't find it, make one up and insert it into
530    the list.
531    s  the session the display should be in
532    n  the index of the display (>=0).  displays in a session are sorted
533       by index, but may be sparse (0, 1, 3, 7)
534    <- the requested display */
535 
536 static _ns_disp *
disp_fetch_or_make(_ns_sess * s,int n)537 disp_fetch_or_make(_ns_sess * s, int n)
538 {
539     _ns_disp *d, *e = NULL, *c;
540 
541     for (c = s->dsps; c && (c->index < n); c = c->next)
542         e = c;
543 
544     if (c && (c->index == n))   /* found it */
545         return c;
546 
547     if (!(d = ns_new_disp()))   /* not there, create new */
548         return NULL;            /* can't create, fail */
549 
550     d->index = n;
551 
552     if ((d->next = c))          /* if not last element... */
553         c->prvs = d;
554     if ((d->prvs = e))          /* if not first element */
555         e->next = d;
556     else                        /* make first */
557         s->dsps = d;
558 
559     d->sess = s;                /* note session on display */
560 
561     if (!d->sess->curr)         /* note as current on session if first display */
562         d->sess->curr = d;
563 
564     return d;
565 }
566 
567 
568 
569 /* get element number from screen-index (latter is sparse, former ain't)
570    screen   the session in question
571    n        the index screen gave us (sparse)
572    <-       the real index (element number in our list of displays) */
573 
574 int
disp_get_real_by_screen(_ns_sess * screen,int n)575 disp_get_real_by_screen(_ns_sess * screen, int n)
576 {
577     _ns_disp *d2 = screen->dsps;
578     int r = 0;
579 
580     while (d2 && d2->index != n) {
581         d2 = d2->next;
582         r++;
583     }
584 #ifdef NS_PARANOID
585     if (!d2)
586         return -1;
587 #endif
588     return r;
589 }
590 
591 
592 
593 /* get screen-index from element number (former is sparse, latter ain't)
594    screen   the session in question
595    n        the real index (element number in our list of displays)
596    <-       the index screen knows (sparse) */
597 
598 int
disp_get_screen_by_real(_ns_sess * screen,int r)599 disp_get_screen_by_real(_ns_sess * screen, int r)
600 {
601     _ns_disp *d2 = screen->dsps;
602 
603     while (d2 && (r-- > 0))
604         d2 = d2->next;
605 #ifdef NS_PARANOID
606     if (!d2)
607         return -1;
608 #endif
609     return d2->index;
610 }
611 
612 
613 
614 /* remove a display from the internal list and release its struct and data
615    disp  the display in question */
616 
617 static void
disp_kill(_ns_disp * d3)618 disp_kill(_ns_disp * d3)
619 {
620     if (d3->prvs) {
621         d3->prvs->next = d3->next;
622         if (d3->sess->curr == d3)
623             d3->sess->curr = d3->prvs;
624     } else {
625         d3->sess->dsps = d3->next;
626         if (d3->sess->curr == d3)
627             d3->sess->curr = d3->next;
628     }
629     if (d3->next)
630         d3->next->prvs = d3->prvs;
631     ns_dst_disp(&d3);
632 }
633 
634 
635 
636 /***************************************************************************/
637 /* attach/detach */
638 /*****************/
639 
640 
641 
642 /* ns_sess_init
643    init an opened session (transmit .screenrc, or whatever)
644    sess  the session
645    <-    error code */
646 
647 int
ns_sess_init(_ns_sess * sess)648 ns_sess_init(_ns_sess * sess)
649 {
650 #ifdef NS_HAVE_SCREEN
651     if ((sess->backend == NS_MODE_NEGOTIATE) || (sess->backend == NS_MODE_SCREEN)) {
652         (void) ns_parse_screenrc(sess, sess->sysrc, NS_ESC_SYSSCREENRC);
653         return ns_parse_screenrc(sess, sess->home, NS_ESC_SCREENRC);
654     }
655 #endif
656     return NS_SUCC;
657 }
658 
659 
660 
661 /* return port number for service TWIN.
662    <-  a port number -- 7754 in all likelihood. */
663 
664 int
ns_get_twin_port(void)665 ns_get_twin_port(void)
666 {
667 #ifdef NS_HAVE_TWIN
668     static int port = 0;
669     struct servent *srv;
670 
671     if (port)
672         return port;
673     /* (fixme) replace with getservbyname_r on systems that have it */
674     srv = getservbyname("twin", "tcp");
675     return (port = (srv ? ntohs(srv->s_port) : TW_INET_PORT));
676 #else
677     return -1;
678 #endif
679 }
680 
681 
682 
683 /* return port number for service SSH (secure shell).
684    <-  a port number -- 22 in all likelihood. */
685 
686 int
ns_get_ssh_port(void)687 ns_get_ssh_port(void)
688 {
689     static int port = 0;
690     struct servent *srv;
691 
692     if (port)
693         return port;
694     /* (fixme) replace with getservbyname_r on systems that have it */
695     srv = getservbyname("ssh", "tcp");
696     return (port = (srv ? ntohs(srv->s_port) : NS_DFLT_SSH_PORT));
697 }
698 
699 
700 
701 
702 /* ns_parse_hop
703    parse a hop-string into a hop-struct
704    h:   one of NULL lclport:fw:fwport fw:fwport lclport:fw
705         if set, describes how to tunnel through a fw to access an URL
706         describing a target behind said firewall
707    <-   a hop struct, or NULL
708 */
709 
710 static _ns_hop *
ns_parse_hop(_ns_sess * s,char * h)711 ns_parse_hop(_ns_sess * s, char *h)
712 {
713     char *p = h, *e;
714     int f = 0, v, lp = 0, fp = 0, delay = 0;
715 
716     if (!h || !*h)
717         return NULL;
718 
719     if ((e = strrchr(h, ','))) {
720         *(e++) = '\0';
721         if (*e)
722             delay = atoi(e);
723     }
724 
725     while (*p && *p != ':')
726         if (!isdigit(*(p++)))
727             f = 1;
728 
729     if (!*p)                    /* fw only */
730         return ns_new_hop(lp, h, fp, delay, s);
731 
732     if (!f) {                   /* lp:fw... */
733         if (!(v = atoi(h)))
734             return NULL;
735         lp = v;
736         e = ++p;
737         while (*e && *e != ':')
738             e++;
739         if (*e) {
740             *(e++) = '\0';
741             if (!(v = atoi(e)))
742                 return NULL;
743             fp = v;
744         }
745     } else {                    /* fw:fp */
746         *(p++) = '\0';
747         if (!(v = atoi(p)))
748             return NULL;
749         fp = v;
750         p = h;
751     }
752     return ns_new_hop(lp, p, fp, delay, s);
753 }
754 
755 
756 
757 /* ns_desc_string
758    c        the string
759    doc      context-info
760    !stdout  the string, in human-readable form */
761 
762 static void
ns_desc_string(char * c,char * doc)763 ns_desc_string(char *c, char *doc)
764 {
765     char *p = c;
766     char buff[1024], *pbuff;
767     size_t len, n;
768 
769     len = sizeof(buff);
770     pbuff = buff;
771 
772     if (doc) {
773         n = snprintf(pbuff, len, "%s: ", doc);
774         len -= n;
775         pbuff += n;
776     }
777 
778     if (!c) {
779         snprintf(pbuff, len, "NULL\n");
780         D_ESCREEN(("%s", buff));
781         return;
782     } else if (!*c) {
783         snprintf(pbuff, len, "empty\n");
784         D_ESCREEN(("%s", buff));
785         return;
786     }
787 
788     while (*p) {
789         if (*p < ' ') {
790             snprintf(pbuff, len, "^%c", *p + 'A' - 1);
791             pbuff += 2;
792             len -= 2;
793         } else {
794             snprintf(pbuff++, len--, "%c", *p);
795         }
796         p++;
797     }
798 
799     D_ESCREEN(("%s\n", buff));
800 
801     return;
802 }
803 
804 
805 
806 /* ns_desc_twin
807    print status of a twin session
808    sess    the session
809    doc     info about the context
810    !stderr  info about the twin session */
811 
812 void
ns_desc_twin(_ns_sess * sess,char * doc)813 ns_desc_twin(_ns_sess * sess, char *doc)
814 {
815 #ifdef HAVE_TWIN
816     if (!sess) {
817         D_ESCREEN(("%s: ns_desc_twin called with broken pointer!\n", doc ? doc : ""));
818         return;
819     }
820     D_ESCREEN(("%s: twin status (%s) is %d-%s, %d-%s\n", doc, sess->twin_str,
821                Tw_Errno(sess->twin),
822                Tw_StrError(sess->twin, Tw_Errno(sess->twin)), Tw_ErrnoDetail(sess->twin), Tw_StrErrorDetail(sess->twin,
823                                                                                                             Tw_Errno(sess->twin),
824                                                                                                             Tw_ErrnoDetail(sess->
825                                                                                                                            twin))));
826 #else
827     USE_VAR(sess);
828     USE_VAR(doc);
829 #endif
830 }
831 
832 
833 
834 /* ns_desc_hop
835    print basic info about a hop (tunnel, firewall).  mostly for debugging.
836    hop:    a hop struct as generated by (eg) ns_attach_by_URL()
837    doc:    info about the context
838  ! stderr: info about the hop */
839 
840 static void
ns_desc_hop(_ns_hop * h,char * doc)841 ns_desc_hop(_ns_hop * h, char *doc)
842 {
843     if (!h) {
844         D_ESCREEN(("%s: ns_desc_hop called with broken pointer!\n", doc ? doc : ""));
845         return;
846     }
847 
848     if (doc)
849         D_ESCREEN(("%s:\n", doc));
850 
851     D_ESCREEN(("tunnel from localhost:%d to %s:%d to %s:%d is %s.  (delay %d, %d ref%s)\n",
852                h->localport, h->fw, h->fwport, h->sess->host, h->sess->port, h->established ? "up" : "down", h->delay, h->refcount,
853                h->refcount == 1 ? "" : "s"));
854 }
855 
856 
857 
858 /* ns_desc_sess
859    print basic info about a session.  mostly for debugging.
860    sess:   a session struct as generated by (eg) ns_attach_by_URL()
861    doc:    info about the context
862  ! stderr: info about the session */
863 
864 static void
ns_desc_sess(_ns_sess * sess,char * doc)865 ns_desc_sess(_ns_sess * sess, char *doc)
866 {
867     if (!sess) {
868         D_ESCREEN(("%s: ns_desc_sess called with broken pointer!\n", doc ? doc : ""));
869         return;
870     }
871     if (sess->where == NS_LCL)
872         D_ESCREEN(("%s: (efuns@%p)\t (user %s) local %s", doc, sess->efuns, sess->user, sess->proto));
873     else {
874         D_ESCREEN(("%s: (efuns@%p)\t %s://%s%s%s@%s",
875                    doc, sess->efuns, sess->proto ? sess->proto : "???", sess->user, sess->pass ? ":" : "",
876                    sess->pass ? sess->pass : "", sess->host));
877         if (sess->port != NS_DFLT_SSH_PORT)
878             D_ESCREEN((":%s", sess->port));
879     }
880     D_ESCREEN(("%c%s\n", sess->where == NS_LCL ? ' ' : '/', sess->rsrc));
881     if (sess->hop)
882         ns_desc_hop(sess->hop, NULL);
883     if (sess->sysrc)
884         D_ESCREEN(("%s: searching for sysrc in %s\n", doc, sess->sysrc));
885     if (sess->home)
886         D_ESCREEN(("%s: searching for usrrc in %s\n", doc, sess->home));
887     D_ESCREEN(("%s: escapes set to ^%c-%c\n", doc, sess->escape + 'A' - 1, sess->literal));
888 #ifdef NS_HAVE_TWIN
889     D_ESCREEN(("%s: twin %s at %p\n", doc, sess->twin_str ? sess->twin_str : "NULL", sess->twin));
890 #endif
891 }
892 
893 
894 
895 /* run a command. uses the terminal's internal run facility.
896    converts system/"char *" to exec/"arg **".
897    efuns: struct of callbacks into the terminal program.
898           ns_run() will fail if no callback to the terminal's "run program"
899           (exec) facility is provided.
900    cmd:   a string to exec
901    <-     whatever the callback returns.  In Eterm, it's a file-descriptor.  */
902 
903 int
ns_run(_ns_efuns * efuns,char * cmd)904 ns_run(_ns_efuns * efuns, char *cmd)
905 {
906     char **args = NULL;
907     char *p = cmd;
908     int c, n = 0, s = 0;
909 
910     if (!efuns || !efuns->execute)
911         goto fail;
912 
913     if (cmd && *cmd) {          /* count args (if any) */
914         D_ESCREEN(("ns_run: executing \"%s\"...\n", cmd));
915         do {
916             n++;
917             while (*p && *p != ' ') {
918                 if (*p == '\"') {
919                     do {
920                         p++;
921                         if (s)
922                             s = 0;
923                         else if (*p == '\\')
924                             s = 1;
925                         else if (*p == '\"')
926                             s = 2;
927                     }
928                     while (*p && s != 2);
929                 }
930                 p++;
931             }
932             while (*p == ' ')
933                 p++;
934         }
935         while (*p);
936 
937         if (!(args = MALLOC((n + 2) * sizeof(char *))))
938             goto fail;
939 
940         for (p = cmd, c = 0; c < n; c++) {
941             args[c] = p;
942             while (*p && *p != ' ') {
943                 if (*p == '\"') {       /* leave quoting stuff together as one arg */
944                     args[c] = &p[1];    /* but remove the quote signs */
945                     do {
946                         p++;
947                         if (s)
948                             s = 0;
949                         else if (*p == '\\')
950                             s = 1;
951                         else if (*p == '\"')
952                             s = 2;
953                     }
954                     while (*p && s != 2);
955                     *p = '\0';
956                 }
957                 p++;
958             }
959             while (*p == ' ')
960                 *(p++) = '\0';
961         }
962         args[c++] = NULL;
963     }
964 
965     n = efuns->execute(NULL, args);
966     if (args)
967         FREE(args);
968     return n;
969 
970   fail:
971     return NS_FAIL;
972 }
973 
974 
975 
976 /* create a call line. used in ns_attach_ssh/lcl
977    tmpl   the template. should contain one %s
978    dflt   the default value
979    opt    the user-supplied value (or NULL)
980    <-     a new MALLOC'd string (or NULL) */
981 
982 static char *
ns_make_call_el(char * tmpl,char * dflt,char * opt)983 ns_make_call_el(char *tmpl, char *dflt, char *opt)
984 {
985     int l, r;
986     char *p;
987 
988     if (tmpl && dflt && *tmpl && strstr(tmpl, "%s")) {
989         l = strlen(tmpl) + (opt ? strlen(opt) : strlen(dflt)) - 1L;
990         if ((p = MALLOC(l))) {
991             r = snprintf(p, l, tmpl, opt ? opt : dflt);
992             if ((r >= 0) && (r < l)) {
993                 return p;
994             }
995             FREE(p);
996         }
997     }
998     return NULL;
999 }
1000 
1001 
1002 
1003 static char *
ns_make_call(_ns_sess * sess)1004 ns_make_call(_ns_sess * sess)
1005 {
1006     char *call, *tmp = NULL, *screen = NULL, *scream = NULL, *screem = NULL, *twin;
1007 
1008 #ifdef NS_HAVE_TWIN
1009     if (sess->backend == NS_MODE_TWIN) {
1010         int r, l;
1011 
1012         tmp = sess->rsrc ? STRDUP(sess->rsrc) : ns_make_call_el(NS_TWIN_OPTS, ":0", sess->twin_str);
1013         l = 1 + strlen(NS_TWIN_CALL) + 2 * strlen(tmp);
1014         if ((twin = MALLOC(l))) {
1015             r = snprintf(twin, l, NS_TWIN_CALL, tmp ? tmp : "", tmp ? tmp : "");
1016 #  ifdef NS_PARANOID
1017             if ((r < 0) || (r > l))
1018                 ns_free(&twin);
1019 #  endif
1020         }
1021         ns_free(&tmp);
1022         return twin;
1023     }
1024 #else
1025     USE_VAR(twin);
1026 #endif
1027     /* unless decidedly in other mode... */
1028     if (sess->backend != NS_MODE_SCREEN)
1029         tmp = scream = ns_make_call_el(NS_SCREAM_CALL, NS_SCREAM_OPTS, sess->rsrc);
1030 #ifdef NS_HAVE_SCREEN
1031     if (sess->backend != NS_MODE_SCREAM)
1032         tmp = screen = ns_make_call_el(NS_SCREEN_CALL, NS_SCREEN_OPTS, sess->rsrc);
1033 #endif
1034     if (sess->backend == NS_MODE_NEGOTIATE) {
1035         int r, l = strlen(NS_SCREEM_CALL) + (scream ? strlen(scream) : 1) + (screen ? strlen(screen) : 1) - 3;
1036 
1037         if ((screem = MALLOC(l))) {
1038             r = snprintf(screem, l, NS_SCREEM_CALL, scream ? scream : ":", screen ? screen : ":");
1039 #ifdef NS_PARANOID
1040             if ((r < 0) || (r > l))
1041                 ns_free(&screem);
1042 #endif
1043         }
1044         tmp = screem;
1045     }
1046     call = ns_make_call_el(NS_WRAP_CALL, tmp, NULL);
1047     ns_free(&screen);
1048     ns_free(&scream);
1049     ns_free(&screem);
1050     return call;
1051 }
1052 
1053 
1054 
1055 /* attach a local session (using screen/scream)
1056    sp  the session
1057    <-  NS_FAIL, or the result of ns_run() */
1058 
1059 static int
ns_attach_lcl(_ns_sess ** sp)1060 ns_attach_lcl(_ns_sess ** sp)
1061 {
1062     _ns_sess *sess;
1063     char *call;
1064     int ret = -1;
1065 
1066     if (!sp || !*sp)
1067         return ret;
1068 
1069     sess = *sp;
1070 
1071     if ((call = ns_make_call(sess))) {
1072         char *c2 = ns_make_call_el("/bin/sh -c \"%s\"", call, NULL);
1073 
1074         ns_free(&call);
1075         if (c2) {
1076             ret = ns_run(sess->efuns, c2);
1077             ns_free(&c2);
1078         }
1079     }
1080     return ret;
1081 }
1082 
1083 
1084 static int
ns_wait_for_socket(int port)1085 ns_wait_for_socket(int port)
1086 {
1087     int tmp_sock, ret = -1;
1088     time_t start_time;
1089 
1090     D_ESCREEN(("Waiting for forwarder to begin listening on port %d.\n", port));
1091     tmp_sock = socket(PF_INET, SOCK_STREAM, 6);
1092     start_time = time(NULL);
1093     if (tmp_sock >= 0) {
1094         struct sockaddr_in addr;
1095         char timeout = 0;
1096 
1097         addr.sin_family = AF_INET;
1098         addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1099         addr.sin_port = htons(port);
1100 
1101         do {
1102             usleep(100);
1103             if ((time(NULL) - start_time) >= NS_TUNNEL_DELAY) {
1104                 timeout = 1;
1105                 break;
1106             }
1107         } while (connect(tmp_sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)));
1108         if (timeout) {
1109             D_ESCREEN((" -> Unable to connect; timeout after %d seconds.\n", NS_TUNNEL_DELAY));
1110             sleep(1);
1111             return -1;
1112         } else {
1113             ret = time(NULL) - start_time;
1114             D_ESCREEN((" -> Connected after %d seconds.\n", ret));
1115         }
1116         close(tmp_sock);
1117     }
1118     return ret;
1119 }
1120 
1121 /* attach a remote session (using screen/scream via ssh)
1122    sp  the session
1123    <-  -1, or the result of ns_run() */
1124 
1125 static int
ns_attach_ssh(_ns_sess ** sp)1126 ns_attach_ssh(_ns_sess ** sp)
1127 {
1128     _ns_sess *sess;
1129     char cmd[NS_MAXCMD + 1];
1130     char esc[] = " -e^\0\0\0";
1131     char *call, *p;
1132     int ret;
1133 
1134     if (!sp || !*sp)
1135         return NS_FAIL;
1136 
1137     sess = *sp;
1138 
1139     p = &esc[3];                /* escapes for screen and compatibles */
1140     if (sess->escape < ' ') {
1141         *p++ = '^';
1142         *p++ = sess->escape + 'A' - 1;
1143     } else
1144         *p++ = sess->escape;    /* this should never happen */
1145     if (sess->literal < ' ') {  /* this should never happen */
1146         *p++ = '^';
1147         *p++ = sess->literal + 'A' - 1;
1148     } else
1149         *p++ = sess->literal;
1150 
1151     call = ns_make_call(sess);
1152 
1153     if (sess->hop) {
1154         if (sess->hop->established == NS_HOP_DOWN) {    /* the nightmare foe */
1155             _ns_efuns *efuns = NULL;
1156 
1157             ret = snprintf(cmd, NS_MAXCMD, "%s %s -p %d -L %d:%s:%d %s@%s",
1158                            NS_SSH_CALL, NS_SSH_TUNNEL_OPTS, sess->hop->fwport, sess->hop->localport, sess->host, sess->port,
1159                            sess->user, sess->hop->fw);
1160             if (ret < 0 || ret > NS_MAXCMD)
1161                 return NS_FAIL;
1162             D_ESCREEN(("Spawning forwarder:  %s\n", cmd));
1163             ns_run(sess->efuns, cmd);
1164             if (NS_EFUN_EXISTS(efuns, sess, NULL, inp_text)) {
1165                 char tmp_buff[] = "Waiting for forwarder...";
1166 
1167                 efuns->inp_text((void *) 1, sess->fd, tmp_buff);
1168             }
1169             if ((ns_wait_for_socket(sess->hop->localport) < 0) && efuns) {
1170                 char tmp_buff[] = "...timed out.";
1171 
1172                 efuns->inp_text((void *) 1, sess->fd, tmp_buff);
1173             }
1174         }
1175         ret = snprintf(cmd, NS_MAXCMD, "%s %s -p %d %s@localhost \"%s%s\"",
1176                        NS_SSH_CALL, NS_SSH_OPTS, sess->hop->localport, sess->user, call, ((sess->backend == NS_MODE_SCREEN)
1177                                                                                           || (sess->backend ==
1178                                                                                               NS_MODE_NEGOTIATE)) ? esc : "");
1179         D_ESCREEN(("Spawning screen session:  %s\n", cmd));
1180     } else {
1181         ret =
1182             snprintf(cmd, NS_MAXCMD, "%s %s -p %d %s@%s \"%s%s\"", NS_SSH_CALL, NS_SSH_OPTS, sess->port, sess->user, sess->host,
1183                      call, ((sess->backend == NS_MODE_SCREEN) || (sess->backend == NS_MODE_NEGOTIATE)) ? esc : "");
1184         D_ESCREEN(("Spawning screen session:  %s\n", cmd));
1185     }
1186     ns_free(&call);
1187 
1188     return (ret < 0 || ret > NS_MAXCMD) ? NS_FAIL : ns_run(sess->efuns, cmd);
1189 }
1190 
1191 
1192 
1193 /* ns_attach_by_sess
1194    attach/create a scream/screen session, locally or over the net.
1195    sess:   a session struct as generated by (eg) ns_attach_by_URL()
1196  ! err:    if non-NULL, variable pointed at will contain an error status
1197    <-      the requested session, or NULL in case of failure.
1198            a session thus returned must be detached/closed later.
1199 */
1200 
1201 _ns_sess *
ns_attach_by_sess(_ns_sess ** sp,int * err)1202 ns_attach_by_sess(_ns_sess ** sp, int *err)
1203 {
1204     _ns_sess *sess;
1205     int err_dummy;
1206 
1207     if (!err)
1208         err = &err_dummy;
1209     *err = NS_INVALID_SESS;
1210 
1211     if (!sp || !*sp)
1212         return NULL;
1213     sess = *sp;
1214 
1215     ns_desc_sess(sess, "ns_attach_by_sess()");
1216 
1217     (void) ns_sess_init(sess);
1218 
1219     switch (sess->where) {
1220         case NS_LCL:
1221             sess->fd = ns_attach_lcl(&sess);
1222             break;
1223         case NS_SU:            /* (fixme) uses ssh, should use su */
1224             /* local session, but for a different uid. */
1225             /* FALL-THROUGH */
1226         case NS_SSH:
1227             if (!sess->delay) {
1228                 sess->delay = NS_INIT_DELAY ? NS_INIT_DELAY : 1;
1229             }
1230             sess->fd = ns_attach_ssh(&sess);
1231             break;
1232         default:
1233             *err = NS_UNKNOWN_LOC;
1234             goto fail;
1235     }
1236 
1237     D_ESCREEN(("ns_attach_by_sess: screen session-fd is %d, ^%c-%c\n", sess->fd, sess->escape + 'A' - 1, sess->literal));
1238 
1239     return sess;
1240 
1241   fail:
1242     return ns_dst_sess(sp);
1243 }
1244 
1245 
1246 
1247 /* ns_attach_by_URL
1248    parse URL into sess struct (with sensible defaults), then pick up/create
1249    said session using ns_attach_by_sess()
1250    url:    URL to create/pick up a session at.
1251            proto://user:password@host.domain:port  (all parts optional)
1252            NULL/empty string equivalent to
1253            screen://current_user@localhost/-xRR
1254    hop:    one of NULL lclport:fw:fwport fw:fwport lclport:fw
1255            if set, describes how to tunnel through a fw to access an URL
1256            describing a target behind said firewall
1257    ef:     a struct containing callbacks into client (resize scrollbars etc.)
1258            while setting those callbacks is optional; omitting the struct
1259            itself seems unwise.
1260  ! err:    if non-NULL, variable pointed at will contain an error status
1261    xd:     pointer to extra-data the terminal app wants to associate with
1262            a session, or NULL
1263    <-      the requested session, or NULL in case of failure.
1264            a session thus returned must be detached/closed later.
1265 */
1266 
1267 _ns_sess *
ns_attach_by_URL(char * url,char * hop,_ns_efuns ** ef,int * err,void * xd)1268 ns_attach_by_URL(char *url, char *hop, _ns_efuns ** ef, int *err, void *xd)
1269 {
1270     int err_dummy;
1271     char *p, *d = NULL;
1272     _ns_sess *sess = ns_new_sess();
1273     struct passwd *pwe = getpwuid(getuid());
1274 
1275     if (!err)
1276         err = &err_dummy;
1277     *err = NS_OOM;
1278 
1279     if (!sess) {
1280         D_ESCREEN(("ns_attach_by_URL: no session...\n"));
1281         return NULL;
1282     }
1283 
1284     D_ESCREEN(("ns_attach_by_URL(%s,%s,%p,%p,%p)\n", url, hop, *ef, err, xd));
1285 
1286     if (url && strlen(url)) {
1287         char *q;
1288 
1289         if (!(d = STRDUP(url)))
1290             goto fail;
1291 
1292         if ((q = strstr(d, "://"))) {   /* protocol, if any */
1293             *q = '\0';
1294             if (!(sess->proto = STRDUP(d)))
1295                 goto fail;
1296             q += 3;
1297         } else
1298             q = d;
1299 
1300         if ((p = strchr(q, '@'))) {     /* user, if any */
1301             char *r;
1302 
1303             if (p != q) {       /* ignore empty user */
1304                 *p = '\0';
1305                 if ((r = strchr(q, ':'))) {     /* password, if any */
1306                     *(r++) = '\0';
1307                     if (!(sess->pass = STRDUP(r)))      /* password may be empty string! */
1308                         goto fail;
1309                 }
1310                 sess->user = STRDUP(q);
1311             }
1312             q = p + 1;
1313         }
1314 
1315         if ((p = strchr(q, '/'))) {     /* rsrc, possibly url-encoded */
1316             *(p++) = '\0';
1317             if (strlen(p)) {
1318                 char *r = p;
1319                 int f;
1320 
1321                 while (*r) {
1322                     if (*r == '+')
1323                         *(r++) = ' ';
1324                     else if ((*r == '%') && (strlen(r) > 2)) {
1325                         long v;
1326                         char *e;
1327                         char b[3];
1328 
1329                         b[0] = r[1];
1330                         b[1] = r[2];
1331                         b[2] = '\0';
1332                         v = strtol(b, &e, 16);
1333                         if (!*e) {
1334                             *(r++) = (char) (v & 0xff);
1335                             memmove(r, &r[2], strlen(&r[2]));
1336                         }
1337                     } else
1338                         r++;
1339                 }
1340                 r = p;
1341                 f = 0;
1342                 while (*r) {
1343                     if (*r == ' ') {    /* Padding between arguments */
1344                         while (*r == ' ')
1345                             r++;
1346                     } else {
1347                         if (*r == '-') {
1348 #  ifdef NS_HAVE_TWIN
1349                             if (!strncmp(NS_TWIN_PARA, &r[1], strlen(NS_TWIN_PARA))) {
1350                                 char *y = strchr(r, '@');
1351 
1352                                 if (y && *++y) {
1353                                     char *z = strchr(y, ':');
1354 
1355                                     if (z) {
1356                                         *z++ = '\0';
1357                                         if (*z)
1358                                             sess->disp = atoi(z);
1359                                         if (sess->disp < 0 || sess->disp > NS_MAX_PORT)
1360                                             sess->disp = 0;
1361                                     }
1362                                     if (strlen(y))
1363                                         sess->host = STRDUP(y);
1364                                 }
1365                             } else
1366 #  endif
1367 #  ifdef NS_HAVE_SCREEN
1368                             if (*(++r) == 'e') {        /* set escape */
1369                                 char x = 0, y = 0;
1370 
1371                                 while (*(++r) == ' ');
1372                                 if ((x = ns_parse_esc(&r)) && (y = ns_parse_esc(&r))) {
1373                                     sess->escape = x;
1374                                     sess->literal = y;
1375                                     sess->escdef = NS_ESC_CMDLINE;
1376                                 }
1377                             } else if (*r == 'c') {     /* alt screenrc */
1378                                 char *rc, *rx;
1379 
1380                                 while (*(++r) == ' ');
1381                                 if ((rx = strchr(r, ' ')))
1382                                     *rx = '\0';
1383                                 if (*r != '/')
1384                                     D_ESCREEN(("URL: path for screen's option -c should be absolute (%s)\n", r));
1385                                 if ((rc = STRDUP(r))) {
1386                                     if (sess->home)     /* this should never happen */
1387                                         FREE(sess->home);
1388                                     D_ESCREEN(("URL: searching for rc in %s\n", rc));
1389                                     sess->home = rc;
1390                                 }
1391                                 if (rx) {
1392                                     r = rx;
1393                                     *rx = ' ';
1394                                 }
1395                             } else
1396 #  endif
1397                             {
1398                                 NOP;
1399                             }
1400                             while (*r && (f || *r != ' ')) {
1401                                 if (*r == '\"')
1402                                     f = 1 - f;
1403                                 r++;
1404                             }
1405                         }
1406                         while (*r && *r != ' ') /* proceed to space */
1407                             r++;
1408                     }
1409                 }
1410 
1411                 if (!(sess->rsrc = STRDUP(p)))
1412                     goto fail;
1413             }
1414         }
1415 
1416         if ((p = strchr(q, ':'))) {     /* port, if any */
1417             *(p++) = '\0';
1418             if (!*p || !(sess->port = atoi(p)) || sess->port > NS_MAX_PORT) {
1419                 *err = NS_MALFORMED_URL;
1420                 D_ESCREEN(("malformed URL...\n"));
1421                 goto fail;
1422             }
1423         }
1424 
1425         if (strlen(q) && !(sess->host = STRDUP(q)))     /* host, if any */
1426             goto fail;
1427 
1428         FREE(d);
1429     }
1430 
1431     sess->where = NS_SSH;
1432 
1433     if (!sess->user) {          /* default user (current user) */
1434         if (!pwe) {
1435             *err = NS_UNKNOWN_USER;
1436             D_ESCREEN(("unknown user...\n"));
1437             goto fail;
1438         }
1439         if (!(sess->user = STRDUP(pwe->pw_name)))
1440             goto fail;
1441     } else if ((sess->host && strcmp(sess->host, "localhost") && strcmp(sess->host, "127.0.0.1")) || (sess->port > 0)) {
1442         pwe = NULL;
1443     } else if (!pwe || strcmp(pwe->pw_name, sess->user)) {      /* user!=current_user */
1444         sess->where = NS_SU;
1445         if (!(pwe = getpwnam(sess->user))) {
1446             *err = NS_UNKNOWN_USER;
1447             D_ESCREEN(("unknown user...\n"));
1448             goto fail;
1449         }
1450     } else {
1451         *err = NS_UNKNOWN_USER;
1452         D_ESCREEN(("unknown user...\n"));
1453         goto fail;
1454     }
1455 
1456 
1457 #ifdef NS_HAVE_SCREEN
1458     if (getenv("SYSSCREENRC")) {        /* $SYSSCREENRC */
1459         if (!(sess->sysrc = STRDUP(getenv("SCREENRC"))))
1460             goto fail;
1461     } else {
1462         char *loc[] = {
1463             "/usr/local/etc/screenrc",  /* official */
1464             "/etc/screenrc",    /* actual (on SuSE) */
1465             "/usr/etc/screenrc",
1466             "/opt/etc/screenrc",
1467             "/etc/screen/screenrc"
1468         };
1469         int n, nloc = sizeof(loc) / sizeof(char *);
1470 
1471         for (n = 0; n < nloc; n++)
1472             if (!access(loc[n], R_OK)) {
1473                 if (!(sess->sysrc = STRDUP(loc[n])))
1474                     goto fail;
1475                 n = nloc;
1476             }
1477     }
1478 
1479     if (getenv("SCREENRC")) {   /* $SCREENRC */
1480         sess->home = STRDUP(getenv("SCREENRC"));
1481     } else if (pwe && !sess->home) {    /* ~/.screenrc */
1482         if ((sess->home = MALLOC(strlen(pwe->pw_dir) + strlen(NS_SCREEN_RC) + 2)))
1483             sprintf(sess->home, "%s/%s", pwe->pw_dir, NS_SCREEN_RC);
1484     }
1485 #endif
1486 
1487     sess->backend = NS_MODE_NEGOTIATE;
1488     if (sess->proto) {
1489 #warning compiling in libscream
1490 #ifdef NS_HAVE_SCREEN
1491         if (!strcmp(sess->proto, "screen")) {
1492             D_ESCREEN(("Using session protocol \"%s\"\n", sess->proto));
1493             sess->backend = NS_MODE_SCREEN;
1494         } else
1495 #warning compiling in support for GNU screen
1496 #endif
1497 #ifdef NS_HAVE_TWIN
1498 #warning compiling in support for twin
1499         if (!strcmp(sess->proto, "twin")) {
1500             char *twd = getenv("TWDISPLAY");
1501 
1502             D_ESCREEN(("Using session protocol \"%s\"\n", sess->proto));
1503             sess->backend = NS_MODE_TWIN;
1504 
1505             /* fall back on TWDISPLAY env var only if host not set yet */
1506             if (twd && (!sess->host || !strlen(sess->host) || !strcmp(sess->host, "localhost"))) {
1507                 char *twdisp = strrchr(twd, ':');
1508 
1509                 if (twdisp) {
1510                     *twdisp++ = '\0';
1511                     if (*twdisp && sess->disp < 0)      /* fall back on TWDISPLAY display */
1512                         sess->disp = atoi(twdisp);
1513                 }               /* fall back on TWDISPLAY host */
1514                 if (((!sess->host) || (!strlen(sess->host))) && strlen(twd) && strcmp(twd, "localhost"))
1515                     sess->host = STRDUP(twd);
1516             }
1517             /* this is ugly, but does the intuitive thing.
1518              * if you specifically want to connect a twin to a non-twin
1519              * port < 20, make it unambiguous by using the syntax
1520              * twin://host.dom:port/-twin@:disp  or
1521              * twin://:port/-twin@host.dom:disp  */
1522             else if (sess->host && sess->disp < 0 && sess->port >= 0 && sess->port < 20) {
1523                 sess->disp = sess->port;
1524                 sess->port = -1;
1525             }
1526         } else
1527 #endif
1528         if (!strcmp(sess->proto, "scream")) {
1529             D_ESCREEN(("Using session protocol \"%s\"\n", sess->proto));
1530             sess->backend = NS_MODE_SCREAM;
1531         } else {
1532             *err = NS_UNKNOWN_PROTO;
1533             D_ESCREEN(("unknown protocol %s...\n", sess->proto));
1534             fprintf(stderr, "protocol \"%s\" not known...\n", sess->proto);
1535             goto fail;
1536         }
1537     } else {
1538         D_ESCREEN(("No session protocol specified.\n"));
1539     }
1540 
1541     if ((sess->disp < 0) || (sess->disp > NS_MAX_PORT))
1542         sess->disp = 0;
1543 
1544 #ifdef NS_HAVE_TWIN
1545     /* do this *before* host-fallback */
1546     if (sess->twin_str)
1547         FREE(sess->twin_str);
1548 
1549     if (sess->twin_str = MALLOC((sess->host ? strlen(sess->host) : 0) + 7))
1550         sprintf(sess->twin_str, "%s:%d", (sess->host ? sess->host : ""), sess->disp);
1551 #endif
1552 
1553     if (!sess->host) {          /* no host */
1554         if (!(sess->host = STRDUP("localhost")))
1555             goto fail;
1556         if (sess->port <= 0) {  /* no host/port */
1557             sess->where = NS_LCL;
1558         }
1559     } else if ((p = strchr(sess->host, '/')))   /* have host */
1560         *p = '\0';
1561 
1562     if (sess->port <= 0) {      /* no port -> default port (SSH) */
1563         if (sess->backend == NS_MODE_TWIN)
1564             sess->port = ns_get_twin_port();
1565         else
1566             sess->port = ns_get_ssh_port();
1567     }
1568 
1569     if (!sess->efuns && ef && *ef) {
1570         sess->efuns = ns_ref_efuns(ef);
1571     }
1572 
1573     sess->userdef = xd;
1574 
1575     if (hop && strlen(hop)) {
1576         sess->hop = ns_parse_hop(sess, hop);
1577         if (sess->hop
1578             && (!strcmp(sess->host, sess->hop->fw) || !strcmp(sess->host, "localhost") || !strcmp(sess->host, "127.0.0.1")))
1579             D_ESCREEN(("ns_attach_by_URL: routing in circles...\n"));
1580     }
1581 
1582     *err = NS_SUCC;
1583 
1584     return ns_attach_by_sess(&sess, err);
1585 
1586   fail:
1587     if (d)
1588         FREE(d);
1589 
1590     D_ESCREEN(("ns_attach_by_URL: fail...\n"));
1591 
1592     return ns_dst_sess(&sess);
1593 }
1594 
1595 
1596 
1597 /* detach a session and release its memory
1598    sess  the session
1599    <-    error code */
1600 int
ns_detach(_ns_sess ** sess)1601 ns_detach(_ns_sess ** sess)
1602 {
1603     ns_desc_sess(*sess, "ns_detach");
1604 #ifdef NS_HAVE_TWIN
1605     if (((*sess)->backend == NS_MODE_TWIN) && (*sess)->twin) {
1606         Tw_Flush((*sess)->twin);
1607         Tw_Close((*sess)->twin);
1608     }
1609 #endif
1610     (void) ns_dst_sess(sess);
1611     return NS_SUCC;
1612 }
1613 
1614 
1615 
1616 /****************************************************************************
1617  ____             _                        _
1618 | __ )  __ _  ___| | __      ___ _ __   __| |
1619 |  _ \ / _` |/ __| |/ /____ / _ \ '_ \ / _` |
1620 | |_) | (_| | (__|   <_____|  __/ | | | (_| |
1621 |____/ \__,_|\___|_|\_\     \___|_| |_|\__,_|
1622 
1623 
1624 backend abstraction (utils)
1625 
1626    this abstracts the backend against the frontend; the terminal-emulator
1627    calls these functions without knowing what the backend is. */
1628 
1629 
1630 
1631 /***************************************************************************/
1632 /* display foo */
1633 /***************/
1634 
1635 
1636 
1637 /* toggle last two displays */
1638 int
ns_tog_disp(_ns_sess * s)1639 ns_tog_disp(_ns_sess * s)
1640 {
1641     if (!s)
1642         return NS_FAIL;
1643 
1644     switch (s->backend) {
1645 #ifdef NS_HAVE_SCREEN
1646       case NS_MODE_SCREEN:
1647 /*        return ns_screen_command(s, "\x01\x01"); */
1648           return ns_statement(s, "other");
1649           break;
1650 #endif
1651         default:
1652             return NS_FAIL;
1653     }
1654 }
1655 
1656 /* go to display #d */
1657 int
ns_go2_disp(_ns_sess * s,int d)1658 ns_go2_disp(_ns_sess * s, int d)
1659 {
1660     if (!s)
1661         return NS_FAIL;
1662     if (s->curr && s->curr->index == d)
1663         return NS_SUCC;
1664 
1665     switch (s->backend) {
1666 #ifdef NS_HAVE_SCREEN
1667       case NS_MODE_SCREEN:
1668           {
1669 /*            char b[] = "\x01_";
1670               b[1] = '0' + d;
1671               return ns_screen_command(s, b); */
1672               char b[] = "select _";
1673               b[7] = '0' + d;
1674               return ns_statement(s, b);
1675           }
1676           break;
1677 #endif
1678 #ifdef NS_HAVE_TWIN
1679         case NS_MODE_TWIN:
1680             {
1681                 tscreen ts = Tw_FirstScreen(s->twin);
1682 
1683                 printf("screen: %p\n", ts);
1684                 while (d-- && ts)
1685                     ts = Tw_NextObj(s->twin, ts);
1686                 if (ts) {
1687                     Tw_RaiseScreen(s->twin, ts);
1688                     return NS_SUCC;
1689                 }
1690             }
1691             break;
1692 #endif
1693         default:
1694             return NS_FAIL;
1695     }
1696 }
1697 
1698 /* toggle monitor mode for disp (if possible). -1 for current disp */
1699 int
ns_mon_disp(_ns_sess * s,int no,int quiet)1700 ns_mon_disp(_ns_sess * s, int no, int quiet)
1701 {
1702     if (!s)
1703         return NS_FAIL;
1704 
1705     D_ESCREEN(("toggling monitoring for display %d\n", no));
1706 
1707     switch (s->backend) {
1708 #ifdef NS_HAVE_SCREEN
1709       case NS_MODE_SCREEN:
1710           if (no >= 0)
1711               ns_go2_disp(s, no);
1712           if (quiet == NS_MON_TOGGLE_QUIET)
1713               s->flags |= NS_SESS_NO_MON_MSG;
1714           else
1715               s->flags &= (~NS_SESS_NO_MON_MSG);
1716 /*        return ns_screen_command(s, "\x01M"); */
1717           return ns_statement(s, "monitor");
1718           break;
1719 #endif
1720     }
1721     return NS_FAIL;
1722 }
1723 
1724 /* scrollback buffer mode (if any) */
1725 int
ns_sbb_disp(_ns_sess * s,int no)1726 ns_sbb_disp(_ns_sess * s, int no)
1727 {
1728     if (!s)
1729         return NS_FAIL;
1730 
1731     switch (s->backend) {
1732 #ifdef NS_HAVE_SCREEN
1733       case NS_MODE_SCREEN:
1734           ns_go2_disp(s, no);
1735 /*        return ns_screen_command(s, "\x01\x1b"); */
1736           return ns_statement(s, "copy");
1737           break;
1738 #endif
1739         default:
1740             return NS_FAIL;
1741     }
1742 }
1743 
1744 /* go to display #+-d */
1745 int
ns_rel_disp(_ns_sess * s,int d)1746 ns_rel_disp(_ns_sess * s, int d)
1747 {
1748     _ns_disp *x;
1749 
1750     if (!s)
1751         return NS_FAIL;
1752     if (!d)
1753         return NS_SUCC;
1754 
1755     if (!s->curr) {
1756         if (!(s->curr = s->dsps)) {
1757             return NS_FAIL;
1758         }
1759     }
1760 
1761     x = s->curr;
1762 
1763     if (d < 0) {
1764         _ns_disp *l;
1765 
1766         for (l = s->dsps; l->next; l = l->next);
1767 
1768         while (d++) {
1769             if (!(x = x->prvs))
1770                 x = l;
1771         }
1772     } else {
1773         while (d--) {
1774             if (!(x = x->next))
1775                 x = s->dsps;
1776         }
1777     }
1778     return ns_go2_disp(s, x->index);
1779 }
1780 
1781 
1782 
1783 /* add a client display and a tab.
1784    s       the session to add to
1785    after   add after this display (0..n).  -1 to add before disp 0.
1786    name    NULL: ask. "": let backend choose name.  else:  set name.  */
1787 
1788 int
ns_add_disp(_ns_sess * s,int after,char * name)1789 ns_add_disp(_ns_sess * s, int after, char *name)
1790 {
1791     int ret = NS_FAIL;
1792 
1793     if (!s) {
1794         return NS_FAIL;
1795     }
1796 
1797     D_ESCREEN(("ns_add_disp: add %s after #%d\n", name, after));
1798 
1799     switch (s->backend) {
1800 #ifdef NS_HAVE_SCREEN
1801       case NS_MODE_SCREEN:
1802           if (after >= 0)
1803               ns_go2_disp(s, after);
1804 /*        if (ns_screen_command(s, "\x01\x03") == NS_SUCC) { */
1805           ret = ns_statement(s, "screen");
1806           if (ret == NS_SUCC) {
1807               D_ESCREEN(("Sent \"screen\" command, now renaming tab.\n"));
1808               if (!name || strlen(name)) {
1809                   ns_ren_disp(s, -2, name);
1810               }
1811               ret = ns_mon_disp(s, -2, NS_MON_TOGGLE_QUIET);
1812           } else {
1813               D_ESCREEN(("ns_statement(screen) returned %d\n", ret));
1814           }
1815           break;
1816 #endif
1817 #ifdef NS_HAVE_TWIN
1818         case NS_MODE_TWIN:
1819             ret = ns_twin_control(s, "twin", TW_MSG_CONTROL_OPEN);
1820             printf("ns_add_disp: twin add window after %d -> %d\n", after, ret);
1821             break;
1822 #endif
1823     }
1824     return ret;
1825 }
1826 
1827 
1828 
1829 /* move client display #fm to display slot #to */
1830 int
ns_mov_disp(_ns_sess * s,int fm,int to)1831 ns_mov_disp(_ns_sess * s, int fm, int to)
1832 {
1833     _ns_disp *d;
1834 
1835     if (!s) {
1836         return NS_FAIL;
1837     }
1838 
1839     if (fm == to)
1840         return NS_SUCC;
1841 
1842     if ((fm < 0) || (to < 0))
1843         return NS_FAIL;
1844 
1845     if (!(d = s->dsps))         /* this should never happen */
1846         return NS_FAIL;
1847 
1848     fm = disp_get_screen_by_real(s, fm);
1849     to = disp_get_screen_by_real(s, to);
1850 
1851     if (fm == to)               /* ??? */
1852         return NS_SUCC;
1853 
1854     switch (s->backend) {
1855 #ifdef NS_HAVE_SCREEN
1856         case NS_MODE_SCREEN:
1857             D_ESCREEN(("ns_mov_disp: move #%d to #%d\n", fm, to));
1858             ns_mov_screen_disp(s, fm, to);
1859             break;
1860 #endif
1861     }
1862     return NS_FAIL;
1863 }
1864 
1865 /* resize display #d to w*h */
1866 int
ns_rsz_disp(_ns_sess * s,int d,int w,int h)1867 ns_rsz_disp(_ns_sess * s, int d, int w, int h)
1868 {
1869     USE_VAR(d);
1870     USE_VAR(w);
1871     USE_VAR(h);
1872 
1873     if (!s) {
1874         return NS_FAIL;
1875     }
1876 
1877     switch (s->backend) {
1878         default:
1879             return NS_FAIL;
1880     }
1881 }
1882 
1883 /* remove display #d */
1884 int
ns_rem_disp(_ns_sess * s,int d,int ask)1885 ns_rem_disp(_ns_sess * s, int d, int ask)
1886 {
1887     char *i = NULL;
1888     int ret = NS_FAIL;
1889 
1890     if (!s) {
1891         return NS_FAIL;
1892     }
1893 
1894     if (!s->curr) {
1895         if (!(s->curr = s->dsps))
1896             return NS_FAIL;
1897     }
1898 
1899     if (d < 0) {
1900         d = s->curr->index;
1901     }
1902 
1903     if (ask) {
1904         (void) ns_inp_dial(s, "Really delete this display?", 1, &i, NULL);
1905         if (!i || !*i)
1906             return NS_FAIL;
1907     }
1908 
1909     if (*i == 'y' || *i == 'Y') {
1910         switch (s->backend) {
1911 #ifdef NS_HAVE_SCREEN
1912           case NS_MODE_SCREEN:
1913               ns_go2_disp(s, d);
1914 /*            ret = ns_screen_command(s, "\x01ky\r"); */
1915               if ((ret = ns_statement(s, "kill")) == NS_SUCC)
1916                   ret = ns_screen_command(s, "y\r");
1917               break;
1918 #endif
1919         }
1920     }
1921 
1922     if (i)
1923         FREE(i);
1924 
1925     return ret;
1926 }
1927 
1928 /* rename display
1929    s    session display is in
1930    d       -1   current display
1931            -2   display was just created and is not in list yet
1932           >=0   index of the display
1933    name   NULL  ask for name
1934          !NULL  the name
1935    <-   error code */
1936 
1937 int
ns_ren_disp(_ns_sess * s,int d,char * name)1938 ns_ren_disp(_ns_sess * s, int d, char *name)
1939 {
1940     char *i = NULL, *n;
1941     size_t l = 0;
1942     int ret = NS_FAIL;
1943 
1944     if (!s) {
1945         return NS_FAIL;
1946     }
1947 
1948     D_ESCREEN(("Renaming display %d to %s\n", d, ((name) ? (name) : ("dialog box input"))));
1949     if (!s->curr) {
1950         if (!(s->curr = s->dsps))
1951             return NS_FAIL;
1952     }
1953 
1954     if (d == -1)
1955         d = s->curr->index;
1956 
1957     if (!name || !*name) {      /* ask */
1958         if (d == -2)
1959             l = 32;             /* dirty, but effective */
1960         else {
1961             i = s->curr->name;
1962             l = strlen(i);
1963         }
1964         D_ESCREEN(("Invoking input dialog; i == %s, l == %lu\n", NONULL(i), l));
1965         (void) ns_inp_dial(s, "Enter a new name for the current display", 12, &i, NULL);
1966         D_ESCREEN((" -> Back, new name is:  \"%s\"\n", NONULL(i)));
1967         if (!i || !*i)
1968             return NS_FAIL;
1969     }
1970 
1971     switch (s->backend) {
1972 #ifdef NS_HAVE_SCREEN
1973         case NS_MODE_SCREEN:
1974             if ((n = MALLOC(strlen(i ? i : name) + l + 1))) {
1975                 if (d >= 0)
1976                     ns_go2_disp(s, d);
1977                 strcpy(&n[l], i ? i : name);    /* copy new name */
1978                 while (l)       /* prepend backspaces */
1979                     n[--l] = '\x08';
1980                 ret = ns_screen_xcommand(s, 'A', n);    /* rename */
1981                 FREE(n);
1982             }
1983             break;
1984 #endif
1985     }
1986 
1987     return ret;
1988 }
1989 
1990 /* log activity in display #d to file "logfile" */
1991 int
ns_log_disp(_ns_sess * s,int d,char * logfile)1992 ns_log_disp(_ns_sess * s, int d, char *logfile)
1993 {
1994     USE_VAR(d);
1995     USE_VAR(logfile);
1996 
1997     if (!s) {
1998         return NS_FAIL;
1999     }
2000 
2001     switch (s->backend) {
2002         default:
2003             return NS_FAIL;
2004     }
2005 }
2006 
2007 
2008 
2009 /***************************************************************************/
2010 /* region/window foo */
2011 /*********************/
2012 
2013 
2014 int
ns_tog_region(_ns_sess * s,_ns_disp * d)2015 ns_tog_region(_ns_sess * s, _ns_disp * d)
2016 {
2017     return ns_magic_disp(&s, &d);
2018 }
2019 
2020 int
ns_go2_region(_ns_sess * s,_ns_disp * d,int n)2021 ns_go2_region(_ns_sess * s, _ns_disp * d, int n)
2022 {
2023     USE_VAR(n);
2024     return ns_magic_disp(&s, &d);
2025 }
2026 
2027 
2028 
2029 int
ns_rel_region(_ns_sess * s,_ns_disp * d,int n)2030 ns_rel_region(_ns_sess * s, _ns_disp * d, int n)
2031 {
2032     int ret = NS_FAIL;
2033 
2034     if (!n)
2035         return NS_SUCC;
2036 
2037     if (ns_magic_disp(&s, &d) == NS_FAIL)
2038         return NS_FAIL;
2039 
2040     switch (s->backend) {
2041 #ifdef NS_HAVE_SCREEN
2042       case NS_MODE_SCREEN:
2043           if (n < 0)
2044               return NS_FAIL;
2045           do {
2046 /*            ret = ns_screen_command(s, "\x01\x09"); */
2047               ret = ns_statement(s, "focus");
2048           } while (--n && (ret == NS_SUCC));
2049           break;
2050 #endif
2051     }
2052     return ret;
2053 }
2054 
2055 
2056 
2057 int
ns_add_region(_ns_sess * s,_ns_disp * d,int after,char * name)2058 ns_add_region(_ns_sess * s, _ns_disp * d, int after, char *name)
2059 {
2060     int ret = NS_FAIL;
2061 
2062     USE_VAR(after);
2063     USE_VAR(name);
2064 
2065     if (ns_magic_disp(&s, &d) == NS_FAIL)
2066         return NS_FAIL;
2067 
2068     switch (s->backend) {
2069 #ifdef NS_HAVE_SCREEN
2070       case NS_MODE_SCREEN:
2071 /*        ret = ns_screen_command(s, "\x01S"); */
2072           ret = ns_statement(s, "split");
2073           break;
2074 #endif
2075     }
2076     return ret;
2077 }
2078 
2079 
2080 
2081 int
ns_rsz_region(_ns_sess * s,_ns_disp * d,int r,int w,int h)2082 ns_rsz_region(_ns_sess * s, _ns_disp * d, int r, int w, int h)
2083 {
2084     USE_VAR(r);
2085     USE_VAR(w);
2086     USE_VAR(h);
2087 
2088     return ns_magic_disp(&s, &d);
2089 }
2090 
2091 int
ns_rem_region(_ns_sess * s,_ns_disp * d,int r,int ask)2092 ns_rem_region(_ns_sess * s, _ns_disp * d, int r, int ask)
2093 {
2094     int ret = NS_FAIL;
2095 
2096     USE_VAR(r);
2097     USE_VAR(ask);
2098 
2099     if (ns_magic_disp(&s, &d) == NS_FAIL)
2100         return NS_FAIL;
2101 
2102     switch (s->backend) {
2103 #ifdef NS_HAVE_SCREEN
2104       case NS_MODE_SCREEN:
2105 /*        ret = ns_screen_command(s, "\x01X"); */
2106           ret = ns_statement(s, "remove");
2107           break;
2108 #endif
2109     }
2110     return ret;
2111 }
2112 
2113 
2114 
2115 int
ns_one_region(_ns_sess * s,_ns_disp * d,int r)2116 ns_one_region(_ns_sess * s, _ns_disp * d, int r)
2117 {
2118     int ret = NS_FAIL;
2119 
2120     USE_VAR(r);
2121 
2122     if (ns_magic_disp(&s, &d) == NS_FAIL)
2123         return NS_FAIL;
2124 
2125     switch (s->backend) {
2126 #ifdef NS_HAVE_SCREEN
2127       case NS_MODE_SCREEN:
2128 /*        ret = ns_screen_command(s, "\x01Q"); */
2129           ret = ns_statement(s, "only");
2130           break;
2131 #endif
2132     }
2133     return ret;
2134 }
2135 
2136 
2137 
2138 int
ns_mov_region(_ns_sess * s,_ns_disp * d,int fm,int to)2139 ns_mov_region(_ns_sess * s, _ns_disp * d, int fm, int to)
2140 {
2141     USE_VAR(fm);
2142     USE_VAR(to);
2143     return ns_magic_disp(&s, &d);
2144 }
2145 
2146 int
ns_ren_region(_ns_sess * s,_ns_disp * d,int r,char * name)2147 ns_ren_region(_ns_sess * s, _ns_disp * d, int r, char *name)
2148 {
2149     USE_VAR(r);
2150     USE_VAR(name);
2151     return ns_magic_disp(&s, &d);
2152 }
2153 
2154 int
ns_log_region(_ns_sess * s,_ns_disp * d,int r,char * logfile)2155 ns_log_region(_ns_sess * s, _ns_disp * d, int r, char *logfile)
2156 {
2157     USE_VAR(r);
2158     USE_VAR(logfile);
2159     return ns_magic_disp(&s, &d);
2160 }
2161 
2162 int
ns_mon_region(_ns_sess * s,_ns_disp * d,int r)2163 ns_mon_region(_ns_sess * s, _ns_disp * d, int r)
2164 {
2165     USE_VAR(r);
2166     return ns_magic_disp(&s, &d);
2167 }
2168 
2169 int
ns_sbb_region(_ns_sess * s,_ns_disp * d,int r)2170 ns_sbb_region(_ns_sess * s, _ns_disp * d, int r)
2171 {
2172     USE_VAR(r);
2173     return ns_magic_disp(&s, &d);
2174 }
2175 
2176 
2177 
2178 /***************************************************************************/
2179 /* session foo */
2180 /***************/
2181 
2182 
2183 
2184 /* scroll horizontally to column x (dummy) */
2185 int
ns_scroll2x(_ns_sess * s,int x)2186 ns_scroll2x(_ns_sess * s, int x)
2187 {
2188     USE_VAR(x);
2189 
2190     if (!s) {
2191         return NS_FAIL;
2192     }
2193 
2194     return NS_FAIL;
2195 }
2196 
2197 /* scroll vertically so line y of the scrollback buffer is the top line */
2198 int
ns_scroll2y(_ns_sess * s,int y)2199 ns_scroll2y(_ns_sess * s, int y)
2200 {
2201     USE_VAR(y);
2202 
2203     if (!s) {
2204         return NS_FAIL;
2205     }
2206 
2207     return NS_FAIL;
2208 }
2209 
2210 /* force an update of the status line */
2211 int
ns_upd_stat(_ns_sess * s)2212 ns_upd_stat(_ns_sess * s)
2213 {
2214     D_ESCREEN(("Forcing update of status line for session 0x%p.\n", s));
2215     if (!s) {
2216         return NS_FAIL;
2217     }
2218 
2219     switch (s->backend) {
2220         /* FIXME:  Causes other problems.  case NS_MODE_NEGOTIATE:*/
2221 #ifdef NS_HAVE_SCREEN
2222         case NS_MODE_SCREEN:
2223             D_ESCREEN(("Calling ns_screen_command(0x%p, %s)\n", s, NS_SCREEN_UPDATE));
2224             return ns_screen_command(s, NS_SCREEN_UPDATE);
2225 #endif
2226         default:
2227             D_ESCREEN(("Failed; backend set to %d\n", s->backend));
2228             return NS_FAIL;
2229     }
2230 }
2231 
2232 /* send a statement to screen.  ("^A:" is automatically prefixed)
2233    the command is parsed before execution (so if e.g. the command changes
2234    the hotkey, we'll know).
2235    s  the session
2236    c  the statement.  if none is given, a dialog is opened.
2237    <- an error-code */
2238 int
ns_statement(_ns_sess * s,char * c)2239 ns_statement(_ns_sess * s, char *c)
2240 {
2241     int ret = NS_FAIL;
2242     char *i = NULL;
2243     char x, y;
2244 
2245     if (!s) {
2246         return NS_FAIL;
2247     }
2248 
2249     y = x = s->escape;
2250 
2251     if (!c || !*c) {
2252         (void) ns_inp_dial(s, "Enter a command to send to the text-window manager", 64, &i,
2253 #ifdef NS_HAVE_SCREEN
2254                            ns_inp_tab
2255 #else
2256                            NULL
2257 #endif
2258             );
2259         if (!i || !*i)
2260             return NS_FAIL;
2261     }
2262 
2263     switch (s->backend) {
2264 #ifdef NS_HAVE_SCREEN
2265         case NS_MODE_SCREEN:
2266             if ((ret = ns_parse_screen_cmd(s, i ? i : c, NS_ESC_INTERACTIVE)) == NS_SUCC) {
2267                 if (s->escape != x) {
2268                     y = s->escape;
2269                     s->escape = x;
2270                 }
2271                 ret = ns_screen_xcommand(s, NS_SCREEN_CMD, i ? i : c);
2272                 D_ESCREEN(("ns_screen_xcommand(%10p, NS_SCREEN_CMD, %s) returned %d.\n",
2273                            s, NONULL(((i) ? (i) : (c))), ret));
2274                 s->escape = y;
2275             } else if (ret == NS_NOT_ALLOWED) {
2276                 ns_inp_dial(s, "Sorry, David, I cannot allow that.", 0, NULL, NULL);
2277             }
2278             break;
2279 #endif
2280         default:
2281             ret = NS_FAIL;
2282     }
2283 
2284     if (i)
2285         FREE(i);
2286 
2287     D_ESCREEN(("Returning %d\n", ret));
2288     return ret;
2289 }
2290 
2291 int
ns_reset(_ns_sess * s,int type)2292 ns_reset(_ns_sess * s, int type)
2293 {
2294     USE_VAR(type);
2295 
2296     if (!s) {
2297         return NS_FAIL;
2298     }
2299 
2300     switch (s->backend) {
2301 #ifdef NS_HAVE_SCREEN
2302         case NS_MODE_SCREEN:
2303             return ns_screen_command(s, NS_SCREEN_INIT);
2304 #endif
2305         default:
2306             return NS_FAIL;
2307     }
2308 }
2309 
2310 char *
ns_get_url(_ns_sess * s,int d)2311 ns_get_url(_ns_sess * s, int d)
2312 {
2313     int r, l;
2314     char *u;
2315     char esc[] = "^_\0";
2316     char lit[] = "^_\0";
2317 
2318     USE_VAR(d);
2319 
2320     if (!s) {
2321         return NULL;
2322     }
2323 
2324     l = ((s->proto) ? strlen(s->proto) + 3 : 0) + strlen(s->user) + 1 + strlen(s->host) + 1 + 5 + 1 +
2325         ((s->rsrc) ? strlen(s->rsrc) : 0) + 7 + (s->name ? strlen(s->name) + 4 : 0) + 1;
2326 
2327     if ((u = MALLOC(l + 1))) {
2328         if (!s->escape) {
2329             esc[0] = '\0';
2330         } else if (s->escape < ' ') {
2331             esc[1] = s->escape + 'A' - 1;
2332         } else {
2333             esc[0] = s->escape;
2334             esc[1] = '\0';
2335         }
2336         if (!s->literal) {
2337             lit[0] = '\0';
2338         } else if (s->literal < ' ') {
2339             lit[1] = s->literal + 'A' - 1;
2340         } else {
2341             lit[0] = s->literal;
2342             lit[1] = '\0';
2343         }
2344         r = snprintf(u, l, "%s%s%s@%s:%d/%s%s%s%s%s%s", s->proto ? s->proto : "", s->proto ? "://" : "", s->user, s->host, s->port,
2345                      ((s->rsrc) ? s->rsrc : ""), ((s->escape) ? "+-e" : ""), esc, ((s->escape) ? lit : ""),
2346                      ((s->name) ? "+-x+" : ""), ((s->name) ? s->name : ""));
2347         D_ESCREEN(("ns_get_url: URL is %s\n", u));
2348         if ((r >= 0) && (r < l)) {
2349             return u;
2350         }
2351         FREE(u);
2352     }
2353 
2354     return NULL;
2355 }
2356 
2357 
2358 
2359 /****************************************************************************
2360  _            _                                 _  __ _
2361 | |___      _(_)_ __        ___ _ __   ___  ___(_)/ _(_) ___
2362 | __\ \ /\ / / | '_ \ _____/ __| '_ \ / _ \/ __| | |_| |/ __|
2363 | |_ \ V  V /| | | | |_____\__ \ |_) |  __/ (__| |  _| | (__
2364  \__| \_/\_/ |_|_| |_|     |___/ .__/ \___|\___|_|_| |_|\___|
2365                                |_|
2366 twin-sepcific routines
2367 
2368   these routines handle a specific backend, the "twin" program. */
2369 
2370 
2371 #ifdef NS_HAVE_TWIN
2372 
2373 int
ns_twin_command(_ns_sess * sess,udat type,byte * port,udat cmd,byte * data)2374 ns_twin_command(_ns_sess * sess, udat type, byte * port, udat cmd, byte * data)
2375 {
2376     udat l = 0;
2377     tmsgport msgport;
2378     tmsg msg;
2379     uldat err;
2380     byte ret;
2381 
2382     if (data)
2383         l = strlen(data);
2384 
2385     if (port) {
2386         if ((msgport = Tw_FindMsgPort(sess->twin, TW_NOID, strlen(port), port))) {
2387             if (type == TW_MSG_USER_CONTROL) {
2388                 tevent_control EventC;
2389 
2390                 if ((msg = Tw_CreateMsg(sess->twin, TW_MSG_USER_CONTROL, l + TW_SIZEOF_TEVENT_CONTROL))) {
2391                     EventC = &msg->Event.EventControl;
2392                     EventC->W = TW_NOID;
2393                     EventC->Code = cmd;
2394                     EventC->Len = l;
2395                     EventC->X = EventC->Y = 0;
2396 
2397                     if (l)
2398                         memcpy(EventC->Data, data, l);
2399 
2400                     if ((ret = Tw_SendMsg(sess->twin, msgport, msg))) {
2401                         printf("controlMsg <- %d\n", ret);
2402                         return NS_SUCC;
2403                     }
2404                 }
2405             } else {
2406                 tevent_clientmsg EventC;
2407 
2408                 if ((msg = Tw_CreateMsg(sess->twin, TW_MSG_USER_CLIENTMSG, l + TW_SIZEOF_TEVENT_CLIENTMSG))) {
2409                     EventC->W = TW_NOID;
2410                     EventC->Code = cmd;
2411                     EventC->Len = l;
2412                     if (l)
2413                         memcpy(EventC->Data.b, data, l);
2414                     if ((ret = Tw_SendMsg(sess->twin, msgport, msg))) {
2415                         printf("clientMsg <- %d\n", ret);
2416                         return NS_SUCC;
2417                     }
2418                 }
2419             }
2420         } else {
2421             D_ESCREEN(("msgport \"%s\" not found\n", port));
2422             return NS_FAIL;
2423         }
2424     } else {
2425         D_ESCREEN(("no msgport given\n"));
2426         return NS_FAIL;
2427     }
2428 
2429     err = TwErrno;
2430     D_ESCREEN(("libTw error: %s%s\n", TwStrError(err), TwStrErrorDetail(err, TwErrnoDetail)));
2431     return NS_FAIL;
2432 }
2433 
2434 #endif
2435 
2436 /****************************************************************************
2437                                                         _  __ _
2438  ___  ___ _ __ ___  ___ _ __        ___ _ __   ___  ___(_)/ _(_) ___
2439 / __|/ __| '__/ _ \/ _ \ '_ \ _____/ __| '_ \ / _ \/ __| | |_| |/ __|
2440 \__ \ (__| | |  __/  __/ | | |_____\__ \ |_) |  __/ (__| |  _| | (__
2441 |___/\___|_|  \___|\___|_| |_|     |___/ .__/ \___|\___|_|_| |_|\___|
2442                                        |_|
2443 screen-specific routines
2444 
2445    these routines handle a specific backend, the GNU "screen" program. */
2446 
2447 
2448 
2449 #ifdef NS_HAVE_SCREEN
2450 
2451 /* ns_swp_screen_disp - swap screen displays
2452    s   session
2453    fm  from (old index)
2454    to  to   (new index)
2455    <-  error code */
2456 
2457 static int
ns_swp_screen_disp(_ns_sess * s,int fm,int to)2458 ns_swp_screen_disp(_ns_sess * s, int fm, int to)
2459 {
2460 /*  char *t2 = "\x01:number %d\r"; */
2461     char *t2 = "number %d";
2462     char b[NS_MAXCMD + 1];
2463     int l;
2464     _ns_disp *d, *d2, *n;
2465 
2466 #  ifdef NS_PARANOID
2467     if ((fm > 9999) || (to > 9999))
2468         return NS_FAIL;
2469 #  endif
2470 
2471     if (!s->curr || s->curr->index != fm) {     /* switch to source disp if necessary */
2472 /*      char *t1 = "\x01'%d\r"; */
2473         char *t1 = "select %d";
2474 
2475         if (!(s->curr = disp_fetch(s, fm))) {
2476             return NS_FAIL;
2477         }
2478 
2479         l = snprintf(b, NS_MAXCMD, t1, fm);
2480 #  ifdef NS_PARANOID
2481         if ((l <= 0) || (l > NS_MAXCMD)) {
2482             return NS_FAIL;
2483         }
2484 #  endif
2485 
2486 /*      (void) ns_screen_command(s, b); */
2487         (void) ns_statement(s, b);
2488     }
2489 
2490     l = snprintf(b, NS_MAXCMD, t2, to);
2491 #  ifdef NS_PARANOID
2492     if ((l <= 0) || (l > NS_MAXCMD)) {
2493         return NS_FAIL;
2494     }
2495 #  endif
2496 
2497 /*  (void) ns_screen_command(s, b); */
2498     (void) ns_statement(s, b);
2499 
2500     d2 = disp_fetch(s, to);
2501 
2502     s->curr->index = to;
2503 
2504     if (d2)                     /* target did exist => screen swapped them, so adjust tgt index */
2505         d2->index = fm;
2506 
2507     d = s->dsps;
2508     while (d) {
2509         if ((n = d->next) && (d->index > n->index)) {   /* must... sort... */
2510             for (d2 = n; d2->next && (d2->index <= d->index); d2 = d2->next);
2511 
2512             if (d->prvs)        /* remove offender from list */
2513                 d->prvs->next = d->next;
2514             else
2515                 s->dsps = d->next;
2516             if (d->next)
2517                 d->next->prvs = d->prvs;
2518 
2519             d->prvs = d2;
2520             if ((d->next = d2->next))
2521                 d2->next->prvs = d;
2522             d2->next = d;
2523 
2524             d = s->dsps;
2525         } else
2526             d = d->next;
2527     }
2528 
2529     return NS_SUCC;
2530 }
2531 
2532 
2533 /* ns_mov_screen_disp - move a screen display to a new position
2534    this does some magic to implement an "insert" using screen's "swap"
2535    s   session
2536    fm  from (old index)
2537    to  to   (new index)
2538    <-  error code */
2539 
2540 static int
ns_mov_screen_disp(_ns_sess * s,int fm,int to)2541 ns_mov_screen_disp(_ns_sess * s, int fm, int to)
2542 {
2543     _ns_efuns *efuns;
2544     _ns_disp *d, *d2 = NULL;
2545     int n = 1;
2546 
2547 #  ifdef NS_PARANOID
2548     if (!(d = s->dsps))         /* this should never happen */
2549         return NS_FAIL;
2550 #  endif
2551 
2552     while (d && d->next) {
2553         n++;
2554         if (d->index == to)
2555             d2 = d;
2556         d = d->next;
2557     }
2558 
2559     if (d2) {                   /* target exist, do the whole enchilada */
2560         if ((d2->prvs) && (d2->prvs->index == fm)) {    /* special case: swap */
2561             ns_swp_screen_disp(s, fm, to);
2562         } else {
2563             while (d && (d->index >= to)) {
2564                 ns_swp_screen_disp(s, d->index, d->index + 1);
2565                 d = d->prvs;
2566             }
2567 
2568             ns_swp_screen_disp(s, fm + ((fm > to) ? 1 : 0), to);
2569 
2570             /* done. now unsparse. */
2571             if (to > fm) {      /* moved right */
2572                 d = s->dsps;
2573                 while (d->index <= fm)
2574                     d = d->next;
2575                 while (d) {
2576                     ns_swp_screen_disp(s, d->index, d->index - 1);
2577                     d = d->next;
2578                 }
2579             }
2580         }
2581     } else if (d->index == to) {        /* kinda ugly : ( */
2582         if ((to - fm) == 1) {   /* swap last two */
2583             ns_swp_screen_disp(s, fm, to);
2584         } else {                /* move before last */
2585             while (d && (d->index >= to)) {     /* renumber */
2586                 ns_swp_screen_disp(s, d->index, d->index + 1);
2587                 d = d->prvs;
2588             }
2589 
2590             ns_swp_screen_disp(s, fm, to);
2591 
2592             /* done. now unsparse. */
2593             d = s->dsps;
2594             while (d->index <= fm)
2595                 d = d->next;
2596             while (d) {
2597                 ns_swp_screen_disp(s, d->index, d->index - 1);
2598                 d = d->next;
2599             }
2600         }
2601     } else {                    /* no target, simple renumber */
2602         ns_swp_screen_disp(s, fm, to);
2603     }
2604 
2605     s->curr = NULL;
2606     ns_dst_dsps(&(s->dsps));
2607 
2608     if (NS_EFUN_EXISTS(efuns, s, NULL, expire_buttons))
2609         efuns->expire_buttons(s->userdef, n);
2610 
2611     ns_upd_stat(s);
2612     return NS_SUCC;
2613 }
2614 
2615 
2616 
2617 /* send a command string to a session, using the appropriate escape-char
2618    sess  the session
2619    cmd   the command string.  escapes must be coded as NS_SCREEN_ESCAPE;
2620          this routine will convert the string to use the escapes actually
2621          used in the session
2622    <-    error code */
2623 
2624 int
ns_screen_command(_ns_sess * sess,char * cmd)2625 ns_screen_command(_ns_sess * sess, char *cmd)
2626 {
2627     _ns_efuns *efuns;
2628     char *c;
2629     int ret = NS_SUCC;
2630 
2631     D_ESCREEN(("Sending command \"%s\"\n", NONULL(cmd)));
2632     if (!cmd || !*cmd) {
2633         return NS_FAIL;
2634     }
2635 
2636     if (NS_EFUN_EXISTS(efuns, sess, NULL, inp_text)) {
2637         if ((c = STRDUP(cmd))) {
2638             char *p;
2639 
2640             for (p = c; *p; p++) {
2641                 if (*p == NS_SCREEN_ESCAPE) {
2642                     *p = sess->escape;
2643                 }
2644             }
2645             ns_desc_string(c, "ns_screen_command: xlated string");
2646             D_ESCREEN(("Calling inp_text(NULL, %d, %s) with ret == %d\n", sess->fd, NONULL(c), ret));
2647             efuns->inp_text(NULL, sess->fd, c);
2648             FREE(c);
2649         } else {
2650             /* out of memory */
2651             ret = NS_OOM;
2652         }
2653     } else {
2654         ret = NS_EFUN_NOT_SET;
2655         D_ESCREEN(("ns_screen_command: sess->efuns->inp_text not set!\n"));
2656     }
2657     D_ESCREEN(("Returning %d\n", ret));
2658     return ret;
2659 }
2660 
2661 
2662 
2663 /* send a single command string to screen, adding the equiv of ^A:
2664    s     the session
2665    cmd   the command string
2666    <-    error code */
2667 
2668 int
ns_screen_xcommand(_ns_sess * s,char prefix,char * cmd)2669 ns_screen_xcommand(_ns_sess * s, char prefix, char *cmd)
2670 {
2671     char *i;
2672     int ret = NS_OOM;
2673 
2674     if ((i = MALLOC(strlen(cmd) + 4))) {
2675         size_t l = strlen(cmd) + 2;
2676 
2677         strcpy(&i[2], cmd);
2678         i[0] = s->escape;
2679         i[1] = prefix;
2680         i[l] = '\n';
2681         i[++l] = '\0';
2682         ret = ns_screen_command(s, i);
2683         FREE(i);
2684     }
2685     D_ESCREEN(("Returning %d\n", ret));
2686     return ret;
2687 }
2688 
2689 
2690 
2691 /* tab completion for screen-commands
2692   !b  current entry (changes)
2693    l  number of characters to compare in current entry
2694    m  maximum number of characters in entry (size of input buffer)
2695    <- error code */
2696 
2697 static int
ns_inp_tab(void * xd,char * b,size_t l,size_t m)2698 ns_inp_tab(void *xd, char *b, size_t l, size_t m)
2699 {
2700     char *sc[] = { "acladd", "addacl", "aclchg", "chacl", "acldel", "aclgrp",
2701         "aclumask", "umask", "activity",
2702         "allpartial", "at", "attrcolor", "autonuke", "bce",
2703         "bell_msg", "bind", "bindkey", "break", "breaktype",
2704         "bufferfile", "c1", "caption", "charset", "chdir",
2705         "clear", "compacthist", "console", "copy",
2706         "crlf", "debug", "defc1", "defautonuke", "defbce",
2707         "defbreaktype", "defcharset", "defflow", "defgr",
2708         "defencoding", "deflog", "deflogin", "defmode",
2709         "defmonitor", "defobuflimit", "defscrollback",
2710         "defshell", "defsilence", "defslowpast", "defutf8",
2711         "defwrap", "defwritelock", "defzombie", "detach",
2712         "dinfo", "displays", "digraph", "dumptermcap",
2713         "escape", "eval", "exec", "fit", "flow", "focus", "gr",
2714         "hardcopy", "hardcopy_append", "hardcopydir",
2715         "height", "help", "history", "ignorecase", "encoding",
2716         "kill", "license", "lockscreen", "log", "logfile",
2717         "login", "logtstamp", "mapdefault", "mapnotnext",
2718         "maptimeout", "markkeys", "meta", "monitor",
2719         "multiuser", "nethack", "next", "nonblock", "number",
2720         "obuflimit", "only", "other", "partial", "password",
2721         "paste", "pastefont", "pow_break", "pow_detach",
2722         "prev", "printcmd", "process", "quit", "readbuf",
2723         "readreg", "redisplay", "remove", "removebuf", "reset",
2724         "resize", "screen", "scrollback", "select",
2725         "sessionname", "setenv", "setsid", "shell",
2726         "shelltitle", "silence", "silencewait", "sleep",
2727         "slowpast", "source", "sorendition", "split", "stuff",
2728         "su", "suspend", "term", "termcap", "terminfo",
2729         "termcapinfo", "unsetenv", "utf8", "vbell",
2730         "vbell_msg", "vbellwait", "verbose", "version",
2731         "width", "windowlist", "windows", "wrap", "writebuf",
2732         "writelock", "xoff", "xon", "zombie"
2733     };
2734 
2735     _ns_efuns *efuns;
2736     _ns_sess *s = (_ns_sess *) xd;
2737     int nsc = sizeof(sc) / sizeof(char *);
2738 
2739     if (NS_EFUN_EXISTS(efuns, s, NULL, inp_tab))
2740         return efuns->inp_tab((void *) s, sc, nsc, b, l, m) < 0 ? NS_FAIL : NS_SUCC;
2741 
2742     D_ESCREEN(("ns_screen_command: sess->efuns->inp_tab not set!\n"));
2743     return NS_EFUN_NOT_SET;
2744 }
2745 
2746 
2747 
2748 /* parse argument to screen's "escape" statement.
2749    x   points to the char to process
2750        screen-manual says this can be one of x ^X \123 or \\ \^ ...
2751   !x   the pointer is advanced to the next segment (from esc to literal etc.)
2752    <-  return as char ('\0' -> fail) */
2753 
2754 char
ns_parse_esc(char ** x)2755 ns_parse_esc(char **x)
2756 {
2757     char r = '\0';
2758 
2759     if (**x == '\\') {
2760         (*x)++;
2761         r = **x;
2762         if (r >= '0' && r <= '7') {     /* octal, otherwise literal */
2763             char b[4] = "\0\0\0";
2764             char *e = *x;
2765             size_t l = 0;
2766 
2767             while ((*e >= '0' && *e <= '7') && (l < 3)) {       /* can't use endptr here : ( */
2768                 e++;
2769                 l++;
2770             }
2771             *x = &e[-1];
2772             while (--l)
2773                 b[l] = *(--e);
2774             r = (char) strtol(b, &e, 8);
2775         }
2776     } else if (**x == '^') {
2777         (*x)++;
2778         r = **x;
2779         if (r >= 'A' && r <= 'Z')
2780             r = 1 + r - 'A';
2781         else if (r >= 'a' && r <= 'z')
2782             r = 1 + r - 'a';
2783         else
2784             r = '\0';
2785     } /* malformed */
2786     else
2787         r = **x;
2788 
2789     if (**x)
2790         (*x)++;
2791     return r;
2792 }
2793 
2794 
2795 
2796 /* ns_parse_screen_cmd
2797    parse a command the user intends to send to the screen program,
2798    either via .screenrc or using ^A:
2799    s       the affected (current) session.  s->current should be set.
2800    p       the command
2801    whence  which parsing stage (screenrc, interactive, ...)
2802    <-  error code */
2803 
2804 int
ns_parse_screen_cmd(_ns_sess * s,char * p,ns_esc_whence whence)2805 ns_parse_screen_cmd(_ns_sess * s, char *p, ns_esc_whence whence)
2806 {
2807     char *p2;
2808     long v1 = -1;
2809 
2810     if (!p || !*p)
2811         return NS_FAIL;
2812 
2813     if ((p2 = strchr(p, ' '))) {        /* first argument */
2814         char *e;
2815 
2816         while (isspace(*p2))
2817             p2++;
2818         v1 = strtol(p2, &e, 0); /* magic conversion mode */
2819         if ((p2 == e) || (v1 < 0))
2820             v1 = -1;
2821     }
2822 #define IS_CMD(b) (strncasecmp(p,b,strlen(b))==0)
2823     if (!p2) {
2824         D_ESCREEN(("screenrc: ignoring  \"%s\" without an argument...\n", p));
2825         /* must return success so it's fowarded to screen in interactive mode.
2826            that way, the user can read the original reply instead of a fake
2827            one from us. */
2828         return NS_SUCC;
2829     } else if (IS_CMD("defescape"))
2830         D_ESCREEN(("screenrc: ignoring  \"defescape\", did you mean \"escape\"?\n"));
2831     else if (IS_CMD("defhstatus") || IS_CMD("hardstatus") || IS_CMD("echo") || IS_CMD("colon") || IS_CMD("wall") ||
2832 #ifdef NS_PARANOID
2833              IS_CMD("nethack") ||
2834 #endif
2835              IS_CMD("info") || IS_CMD("time") || IS_CMD("title") || IS_CMD("lastmsg") || IS_CMD("msgwait") || IS_CMD("msgminwait")) {
2836         D_ESCREEN(("screenrc: ignoring  \"%s\", not applicable...\n", p));
2837         return NS_NOT_ALLOWED;
2838     } else if (IS_CMD("escape")) {
2839         char x = 0, y = 0;
2840 
2841         if ((x = ns_parse_esc(&p2)) && (y = ns_parse_esc(&p2))) {
2842             if (s->escdef == NS_ESC_CMDLINE) {
2843                 D_ESCREEN(("screenrc: ignoring  \"escape\"; overridden on command-line...\n", x, y));
2844                 return NS_NOT_ALLOWED;
2845             } else {
2846                 s->escape = x;
2847                 s->literal = y;
2848                 s->escdef = whence;
2849                 return NS_SUCC;
2850             }
2851         } else
2852             D_ESCREEN(("screenrc: ignoring  \"escape\" because of invalid arguments %o %o...\n", x, y));
2853     } else if (IS_CMD("defscrollback")) {
2854         if (v1 < NS_SCREEN_DEFSBB)
2855             D_ESCREEN(("screenrc: ignoring  \"%s\" for value < %d...\n", p, NS_SCREEN_DEFSBB));
2856         else {
2857             s->dsbb = v1;
2858             return NS_SUCC;
2859         }
2860     } else if (IS_CMD("scrollback")) {
2861         if (v1 < NS_SCREEN_DEFSBB)
2862             D_ESCREEN(("screenrc: ignoring  \"%s\" for value < %d...\n", p, NS_SCREEN_DEFSBB));
2863         else {
2864             if (!s->curr)
2865                 s->curr = s->dsps;
2866             if (!s->curr)
2867                 D_ESCREEN(("screenrc: ignoring  \"%s\", cannot determine current display!?...\n", p));
2868             else
2869                 s->curr->sbb = v1;
2870             return NS_SUCC;
2871         }
2872     } else {
2873         D_ESCREEN(("screenrc: bored now \"%s\"\n", p));
2874         return NS_SUCC;
2875     }
2876     return NS_FAIL;
2877 }
2878 
2879 
2880 
2881 /* ns_parse_screen_key
2882    parse and forward a screen-hotkey
2883    s    the session to forward to
2884    c    the character following the escape-char.  (when we got here,
2885         we already got (and threw out) a screen-escape, so we'll have
2886         to also send one if we ever forward c to the screen program.
2887    <-   error code */
2888 
2889 int
ns_parse_screen_key(_ns_sess * s,char c)2890 ns_parse_screen_key(_ns_sess * s, char c)
2891 {
2892     int ret = NS_SUCC;
2893     char b[3];
2894 
2895     b[0] = s->escape;
2896     b[1] = c;
2897     b[2] = '\0';
2898 
2899     if (c < 27)
2900         D_ESCREEN(("screen_key: ^%c-^%c %d\n", s->escape + 'A' - 1, c + 'A' - 1, c));
2901     else
2902         D_ESCREEN(("screen_key: ^%c-%c %d\n", s->escape + 'A' - 1, c, c));
2903 
2904     switch (c) {
2905         case NS_SCREEN_CMD:    /* send command (statement) to screen server */
2906             ns_statement(s, NULL);
2907             break;
2908         case NS_SCREEN_RENAME: /* rename current display */
2909             ret = ns_ren_disp(s, -1, NULL);
2910             break;
2911         case NS_SCREEN_KILL:
2912             ret = ns_rem_disp(s, -1, TRUE);
2913             break;
2914         default:
2915             ret = ns_screen_command(s, b);
2916     }
2917 
2918     return ret;
2919 }
2920 
2921 
2922 
2923 /* ns_parse_screen_interactive
2924    parse a whole string that may contain screen-escapes that should be
2925    handled interactively (that should open dialog boxes etc.).
2926    this will normally be called by menus, buttons etc. that want to send
2927    input without generating X events for the keystrokes (real keystrokes
2928    do not come through here; the keyboard-handler should call
2929    ns_parse_screen_key() directly when it sees the session's escape-char).
2930    s   the session in question
2931    c   the string to parse
2932    <-  error code */
2933 
2934 int
ns_parse_screen_interactive(_ns_sess * sess,char * c)2935 ns_parse_screen_interactive(_ns_sess * sess, char *c)
2936 {
2937     char *s, *p, *o;
2938 
2939     if (!c || !*c)
2940         return NS_FAIL;
2941 #ifdef NS_PARANOID
2942     if (!(s = o = STRDUP(c)))
2943         return NS_FAIL;
2944 #else
2945     s = c;
2946 #endif
2947 
2948     p = s;
2949 
2950     while ((p = strchr(s, NS_SCREEN_ESCAPE))) {
2951         *p = '\0';
2952         (void) ns_screen_command(sess, s);
2953         *p = NS_SCREEN_ESCAPE;
2954         if (*(++p))
2955             ns_parse_screen_key(sess, *(p++));
2956         s = p;
2957     }
2958     (void) ns_screen_command(sess, s);
2959 
2960 #ifdef NS_PARANOID
2961     FREE(o);
2962 #endif
2963 
2964     return NS_SUCC;
2965 }
2966 
2967 
2968 
2969 /* ns_weird_screen -- damage control
2970    screen        the offending session
2971    doc           string specifying the context
2972   !stderr        a description of the problem
2973   !err_inhibit   the problem-type is marked so we don't rewarn.
2974    <-            error code (always NS_FAIL) */
2975 
2976 static int
ns_screen_weird(_ns_sess * screen,long type,char * doc)2977 ns_screen_weird(_ns_sess * screen, long type, char *doc)
2978 {
2979     if (!(err_inhibit & type)) {
2980         err_inhibit |= type;
2981         ns_desc_sess(screen, "ns_screen_weird");
2982         fprintf(stderr, "parse_screen: %s (%ld) screen sent weird stuff.\n"
2983                 "This should never happen. It is assumed that you use a\n"
2984                 "rather unusual configuration for \"screen\".   Please\n"
2985                 "send the result of 'screen --version' to <scream@azundris.com>\n"
2986                 "(together with your ~/.screenrc and /etc/screenrc if present).\n"
2987                 "If at all possible, please also run 'Eterm -e screen' and make\n"
2988                 "a screenshot of the offending window (and the window only, the\n"
2989                 "beauty of your desktop is not relevant to this investigation. : ).\n", doc, type);
2990     }
2991     (void) ns_upd_stat(screen);
2992     return NS_FAIL;
2993 }
2994 
2995 
2996 
2997 /* ns_parse_screenrc -- read the user's screenrc (if we can find it),
2998    parse it (we need to know if she changes the escapes etc.), and
2999    send it to the actually screen
3000    s       the session
3001    fn      name of the file in question
3002    whence  which screenrc are we in?
3003    <-      error code */
3004 
3005 static int
ns_parse_screenrc(_ns_sess * s,char * fn,ns_esc_whence whence)3006 ns_parse_screenrc(_ns_sess * s, char *fn, ns_esc_whence whence)
3007 {
3008     int fd = -1;
3009     char *rc = NULL;
3010 
3011     if (fn) {
3012         struct stat st;
3013         ssize_t rd = 0;
3014 
3015         if ((fd = open(fn, 0)) >= 0) {
3016             if (!fstat(fd, &st)) {
3017                 if ((rc = MALLOC(st.st_size + 1))) {
3018                     char *p;
3019 
3020                     while (((rd = read(fd, rc, st.st_size)) < 0) && (errno == EINTR));
3021                     if (rd < 0)
3022                         goto fail;
3023                     rc[rd] = '\0';
3024 
3025                     p = rc;
3026                     while (*p) {
3027                         char *p2 = p, *n;
3028                         int f = 0;
3029 
3030                         while (*p2 && *p2 != '\n' && *p2 != '\r')       /* find EOL */
3031                             p2++;
3032                         n = p2;
3033                         while (*n == '\r' || *n == '\n')        /* delete EOL */
3034                             *(n++) = '\0';
3035                         while (isspace(*p))
3036                             p++;
3037 
3038                         p2 = p; /* on first non-white */
3039                         while (*p2) {
3040                             if (*p2 == '\\') {
3041                                 p2++;
3042                                 if (*p2)        /* sanity check */
3043                                     p2++;
3044                             } else {
3045                                 if (*p2 == '\"')
3046                                     f = 1 - f;
3047                                 if (!f && *p2 == '#')   /* comment, kill to EOL */
3048                                     *p2 = '\0';
3049                                 else
3050                                     p2++;
3051                             }
3052                         }
3053 
3054                         if (strlen(p))  /* any commands in line? */
3055                             ns_parse_screen_cmd(s, p, whence);
3056                         p = n;  /* done, next line */
3057                     }
3058                     FREE(rc);
3059                     close(fd);
3060                     return NS_SUCC;
3061                 }
3062             }
3063         }
3064     }
3065 
3066   fail:
3067     if (fd >= 0)
3068         close(fd);
3069     if (rc)
3070         FREE(rc);
3071     return NS_FAIL;
3072 }
3073 
3074 
3075 
3076 
3077 /* parse a message (not a display-list) set by the "screen" program
3078    screen   the session associated with that instance of screen,
3079             as returned by ns_attach_by_URL() and related.
3080             the session must contain a valid struct of callbacks (efuns),
3081             as certain functionalities ("add a tab", "show status message")
3082             may be called from here.
3083    p        the offending message-line
3084   !         mode of operation may be modified using screen->flags
3085    <-       returns an error code. */
3086 
3087 static int
ns_parse_screen_msg(_ns_sess * screen,char * p)3088 ns_parse_screen_msg(_ns_sess * screen, char *p)
3089 {
3090     _ns_efuns *efuns;
3091     char *p2;
3092     char vdate[33], vtype[3], vrem[17], win[64];
3093     int ma, mi, mu, ret = NS_SUCC, type, n;
3094     size_t l;
3095 
3096     if (!p)
3097         return NS_FAIL;
3098 
3099     if (*p == ':')
3100         p++;
3101     while (isspace(*p))
3102         p++;
3103 
3104     D_ESCREEN(("got \"%s\"\n", p));
3105 
3106     type = (strlen(p) > 1) ? NS_SCREEN_STATUS : NS_SCREEN_ST_CLR;
3107 
3108     if (type == NS_SCREEN_ST_CLR) {
3109         if (NS_EFUN_EXISTS(efuns, screen, NULL, err_msg))
3110             ret = efuns->err_msg(NULL, type, "");
3111     }
3112     /* a screen display can disappear because the program in it dies, or
3113        because we explicitly ask screen to kill the display.  in the latter
3114        case, screen messages upon success.  rather than explicitly killing
3115        the disp-struct here, we force a status-line update instead (in which
3116        the status-line checker will notice the disp has gone, and delete it
3117        from the struct-list).  this way, we won't need to duplicate the
3118        delete-logic here. */
3119     else if (!strncmp(p, "Window ", strlen("Window ")) && (p2 = strrchr(p, ' ')) && !strcmp(p2, " killed.")) {
3120         ret = ns_upd_stat(screen);
3121         p = NULL;
3122     } else if (!strncmp(p, NS_SCREEN_SESS_T, strlen(NS_SCREEN_SESS_T))) {
3123         if (screen->name) {
3124             FREE(screen->name);
3125         }
3126         if ((screen->name = STRDUP(&p[strlen(NS_SCREEN_SESS_T)]))) {
3127             size_t lsn = strlen(screen->name);
3128 
3129             if (lsn) {
3130                 screen->name[--lsn] = '\0';
3131             }
3132             D_ESCREEN(("ns_parse_screen_msg: session is \"%s\"\n", screen->name));
3133         }
3134         p = NULL;
3135     } else if (!strcmp(p, "New screen...") ||
3136                !strncmp(p, "msgwait", strlen("msgwait")) ||
3137                !strncmp(p, "msgminwait", strlen("msgminwait")) ||
3138                !strcmp(p, "Press ^@ to destroy or ^@ to resurrect window")
3139                || !strcmp(p, "Aborted because of window size change.")) {
3140         p = NULL;
3141     } else if ((screen->flags & NS_SESS_NO_MON_MSG) &&
3142              ((sscanf(p, "Window %d (%s) is now being monitored for all activity.", &n, win) == 2) ||
3143               (sscanf(p, "Window %d (%s) is no longer being monitored for activity.", &n, win) == 2))) {
3144         D_ESCREEN(("activity toggled quietly for window %d-%s\n", n, win));
3145         p = NULL;
3146         screen->flags = (screen->flags & ~NS_SESS_NO_MON_MSG);  /* reset mute flag */
3147     } else if (!strncmp(p, NS_SCREEN_ACT_T, l = strlen(NS_SCREEN_ACT_T))) {
3148         if (NS_EFUN_EXISTS(efuns, screen, NULL, upd_disp)) {
3149             int inx, button;
3150             _ns_disp *d;
3151 
3152             p += l;
3153             inx = atoi(p);
3154             button = disp_get_real_by_screen(screen, inx);
3155             if ((d = disp_fetch(screen, inx))) {
3156                 D_ESCREEN(("activity in window %d-%s (button %d)\n", inx, d->name, button));
3157                 d->flags |= NS_SCREAM_ACT;
3158                 efuns->upd_disp(screen->userdef, button, d->flags, NULL);
3159             } else {
3160                 D_ESCREEN(("activity in unknown window %d (button %d)...\n", inx, button));
3161             }
3162         }
3163         p = NULL;
3164     } else if (sscanf(p, NS_SCREEN_VERSION_T, vtype, &ma, &mi, &mu, vrem, vdate) == 6) {
3165         if (!strcmp("en", vtype))
3166             screen->backend = NS_MODE_SCREEN;
3167         else if (!strcmp("am", vtype))
3168             screen->backend = NS_MODE_SCREAM;
3169         p = NULL;
3170         D_ESCREEN(("ns_parse_screen_msg: scre%s %d.%2d.%2d %s a/o %s -> mode %d\n", vtype, ma, mi, mu, vrem, vdate,
3171                    screen->backend));
3172     } else if (!strcmp(p, NS_SCREEN_NO_DEBUG)) {
3173         p = "debug info was not compiled into \"screen\"...";
3174     } else if (!strncmp(p, NS_SCREEN_DK_CMD_T, strlen(NS_SCREEN_DK_CMD_T))) {
3175         p[strlen(p) - 1] = '\0';
3176         p2 = &p[strlen(NS_SCREEN_DK_CMD_T)];
3177         p = "unknown screen statement ignored";
3178     }
3179     if (p) {                    /* status. send to status-line or dialog or whatever */
3180         if (NS_EFUN_EXISTS(efuns, screen, NULL, err_msg)) {
3181             ret = efuns->err_msg(NULL, type, p);
3182         }
3183     }
3184     return ret;
3185 }
3186 
3187 
3188 
3189 /* parse the "hardstatus"-line of screens.
3190    this is, and unfortunately has to be, a ton of heuristics.
3191    I'm pretty sure there will be (esoteric) situations that are not handled
3192    (correctly) by this code, particularly in connection with more sessions
3193    than can be enumerated in the status-line (we do have workarounds for
3194    that case, they're just not very well tested yet).
3195    do not touch this unless you are absolutely sure you know what you're
3196    doing.   2002/05/01  Azundris  <scream@azundris.com>
3197 
3198    screen   the session associated with that instance of screen,
3199             as returned by ns_attach_by_URL() and related.
3200             the session must contain a valid struct of callbacks (efuns),
3201             as certain functionalities ("add a tab", "show status message")
3202             may be called from here.
3203    force    the terminal wants us to update.  if it doesn't, we may see
3204             fit to do so anyway in certain cases.
3205    width    the terminal's width in columns (ie that of the status line)
3206    p        the pointer to the status line.  may point into the terminal's
3207             line buffer if that holds plain text data (not interleaved with
3208             colour- and boldness-data)
3209    <-       returns an error code. */
3210 
3211 int
ns_parse_screen(_ns_sess * screen,int force,int width,char * p)3212 ns_parse_screen(_ns_sess * screen, int force, int width, char *p)
3213 {
3214     char *p4, *p3, *p2;         /* pointers for parser magic */
3215     static const char *p5 = NS_SCREEN_FLAGS;
3216     static int l = sizeof(NS_SCREEN_FLAGS);
3217     size_t status_blanks = 0;   /* status-bar overflow? */
3218     int ret = NS_SUCC, tmp, parsed,     /* no of *visible* elements in status line */
3219         n,                      /* screen's index (immutable, sparse) */
3220         r;                      /* real index (r'th element) */
3221     _ns_efuns *efuns;
3222     _ns_disp *disp = NULL, *d2 = NULL;
3223 
3224     if (!screen || !p || !width)
3225         return NS_FAIL;
3226 
3227     if (!force && screen->timestamp)
3228         return NS_SUCC;
3229 
3230     D_ESCREEN(("ns_parse_screen(0x%08x, %d, %d, \"%s\")\n", screen, force, width,
3231                safe_print_string(p, width)));
3232     if ((p = STRDUP(p))) {
3233         _ns_parse pd[NS_MAX_DISPS];
3234 
3235         p2 = &p[width - 1];
3236         if (*p2 == ' ') {
3237             while (p2 > p && *p2 == ' ') {
3238                 status_blanks++;
3239                 *(p2--) = '\0';
3240             }                   /* p2 now points behind last item */
3241         } else {
3242             *p2 = 0;            /* make darn sure it's NUL-terminated */
3243         }
3244 
3245         D_ESCREEN(("parse_screen: screen sends \"%s\" (%d)\n", p, strlen(p)));
3246 
3247         if (strlen(p) < 2) {    /* special case: display 0 */
3248             disp = screen->dsps;        /* might not get a status-line in d0! */
3249             if (disp && !(disp->flags & NS_SCREAM_CURR)) {      /* flags need updating */
3250                 disp->flags |= NS_SCREAM_CURR;  /* set flag to avoid calling inp_text */
3251                 disp->flags &= ~NS_SCREAM_ACT;
3252                 while (disp->next) {
3253                     disp->flags &= ~NS_SCREAM_CURR;
3254                     disp = disp->next;
3255                 }
3256                 ret = ns_upd_stat(screen);
3257                 D_ESCREEN(("parse_screen: qeueing update\n"));
3258             } /* more than once */
3259             else if (!screen->timestamp) {
3260                 /* send init string the first time around, just to be on
3261                    the safe side.  we could send it before entering this
3262                    function for the first time, but that would break if
3263                    escapes or screenrc were set from the
3264                    command-line. don't ask. */
3265 
3266                 D_ESCREEN(("parse_screen: preparing screen...\n"));
3267 
3268                 if (screen->delay > 0) {
3269                     screen->timestamp = time(NULL) + screen->delay;
3270                     if (NS_EFUN_EXISTS(efuns, screen, NULL, waitstate)) {
3271                         ret = efuns->waitstate(NULL, screen->delay * 1000);
3272                     }
3273                     if (screen->where == NS_LCL) {
3274                         D_ESCREEN(("Sending update request.\n"));
3275                         ns_upd_stat(screen);
3276                     } else {
3277                         D_ESCREEN(("Not sending update request (%d).\n", screen->where));
3278                     }
3279                 } else {
3280                     if (screen->where == NS_LCL) {
3281                         D_ESCREEN(("Sending init request.\n"));
3282                         (void) ns_screen_command(screen, NS_SCREEN_INIT);
3283                     } else {
3284                         D_ESCREEN(("Not sending init request (%d).\n", screen->where));
3285                     }
3286                     screen->timestamp = 1;
3287                 }
3288             } else if ((screen->timestamp > 1) && (time(NULL) >= screen->timestamp)) {
3289                 (void) ns_screen_command(screen, NS_SCREEN_INIT);
3290                 screen->timestamp = 1;
3291                 D_ESCREEN(("parse_screen: resetting screen...\n"));
3292             } else {
3293                 D_ESCREEN(("parse_screen: nothing to do in exception, updating anyways...\n"));
3294                 ret = ns_upd_stat(screen);
3295             }
3296             FREE(p);
3297             return ret;
3298         } else if (screen->backend == NS_MODE_NEGOTIATE) {
3299             /* I can't believe we haven't decided on a backend yet!  Ask! */
3300             (void) ns_screen_command(screen, NS_SCREEN_VERSION);
3301             (void) ns_screen_command(screen, NS_SCREEN_SESSION);
3302             screen->timestamp = time(NULL);
3303         }
3304 
3305         p3 = p;
3306         while (isspace(*p3))    /* skip left padding */
3307             p3++;
3308 
3309         if (isdigit(*p3)) {     /* list of displays */
3310             parsed = r = 0;
3311             do {
3312                 n = atoi(p3);
3313                 pd[parsed].name = NULL;
3314                 pd[parsed].screen = n;
3315                 pd[parsed].real = r++;
3316 
3317                 while (isdigit(*p3))    /* skip index */
3318                     p3++;
3319 
3320                 pd[parsed].flags = 0;   /* get and skip flags */
3321                 while (*p3 && *p3 != ' ') {
3322                     for (n = 0; n < l; n++) {
3323                         if (*p3 == p5[n]) {
3324                             pd[parsed].flags |= (1 << n);
3325                             break;
3326                         }
3327                     }
3328                     p3++;
3329                 }
3330 
3331                 if (*p3 == ' ') {       /* skip space, read name */
3332                     *(p3++) = '\0';
3333                     p4 = p3;
3334                     while (p3[0] && p3[1] && (p3[0] != ' ' || p3[1] != ' '))
3335                         p3++;
3336                     if (p3[0] == ' ') {
3337                         *(p3++) = '\0';
3338                         while (isspace(*p3))
3339                             p3++;
3340                     }
3341                     pd[parsed++].name = p4;
3342                     if (parsed >= NS_MAX_DISPS)
3343                         p3 = &p3[strlen(p3)];
3344                 } /* out of mem => skip remainder */
3345                 else
3346                     p3 = &p3[strlen(p3)];       /* weirdness  => skip remainder */
3347             } while (*p3);
3348 
3349             for (r = 0; r < parsed; r++) {
3350                 n = pd[r].screen;
3351                 disp = disp_fetch(screen, n);
3352 
3353                 if (pd[r].flags & NS_SCREAM_CURR) {
3354                     pd[r].flags &= ~NS_SCREAM_ACT;
3355                 }
3356 
3357                 if (!disp) {    /* new display */
3358                     if (!(disp = disp_fetch_or_make(screen, n)) || !(disp->name = STRDUP(pd[r].name))) {
3359                         D_ESCREEN(("parse_screen: out of memory in new_display(%d)\n", n));
3360                         ret = NS_FAIL;
3361                     } else {
3362                         if (NS_EFUN_EXISTS(efuns, screen, NULL, ins_disp))
3363                             ret = efuns->ins_disp(screen->userdef, pd[r].real - 1, pd[r].screen, disp->name);
3364                     }
3365                 } else {
3366                     int fl = (disp->flags & ~NS_SCREAM_CURR) | (pd[r].flags & NS_SCREAM_MASK);
3367 
3368                     if ((fl & (NS_SCREAM_CURR | NS_SCREAM_ACT)) == (NS_SCREAM_CURR | NS_SCREAM_ACT))
3369                         fl &= ~NS_SCREAM_ACT;
3370 
3371                     if ((tmp = strcmp(disp->name, pd[r].name)) ||       /* upd display */
3372                         (disp->flags != fl)) {
3373                         if (tmp) {
3374                             /* Don't free disp->name; ns_inp_dial() already did! */
3375                             if (!(disp->name = STRDUP(pd[r].name))) {
3376                                 FREE(p);
3377                                 return NS_FAIL;
3378                             }
3379                         }
3380                         if (disp->flags != fl) {
3381                             if (pd[r].flags & NS_SCREAM_CURR)
3382                                 disp->sess->curr = disp;
3383                             disp->flags = fl;
3384                         } else
3385                             fl = -1;
3386                         if (NS_EFUN_EXISTS(efuns, screen, NULL, upd_disp))
3387                             ret = efuns->upd_disp(screen->userdef, r, fl, (!tmp) ? NULL : disp->name);
3388                     }
3389                 }
3390 
3391                 /* remove any displays from list that have disappeared
3392                    from the middle of the status-line */
3393                 if (!d2 || d2->next != disp) {  /* remove expired displays */
3394                     _ns_disp *d3 = disp->prvs, *d4;
3395 
3396                     while (d3 && d3 != d2) {
3397                         D_ESCREEN(("parse_screen: remove expired middle %d \"%s\"...\n", d3->index, d3->name));
3398                         d4 = d3->prvs;
3399                         if (NS_EFUN_EXISTS(efuns, screen, NULL, del_disp))
3400                             ret = efuns->del_disp(screen->userdef, disp_get_real_by_screen(screen, d3->index));
3401                         disp_kill(d3);
3402                         d3 = d4;
3403                     }
3404                 }
3405                 d2 = disp;
3406             }
3407 
3408 
3409 
3410 #ifdef NS_PARANOID
3411             if (!r) {
3412                 D_ESCREEN(("parse_screen: no elements parsed (!r)...\n"));
3413                 FREE(p);
3414                 return ns_screen_weird(screen, NS_ERR_WEIRDSCREEN, "no elements parsed (!r)...");
3415             } else
3416 #endif
3417                 /* kill overhang (o/t right) if status-line isn't side-scrolling
3418                    (as it will if not all the disp names fit in the status-line) */
3419             if (disp->next && status_blanks > (strlen(disp->next->name) + 6)) {
3420                 _ns_disp *d3 = disp;
3421 
3422                 for (disp = disp->next; disp;) {
3423                     D_ESCREEN(("parse_screen: remove expired right %d \"%s\"...\n", disp->index, disp->name));
3424                     d2 = disp;
3425                     if (d2->sess->curr == d2)
3426                         d2->sess->curr = d3;
3427                     disp = disp->next;
3428                     if (NS_EFUN_EXISTS(efuns, screen, NULL, del_disp))
3429                         ret = efuns->del_disp(screen->userdef, disp_get_real_by_screen(screen, d2->index));
3430                     disp_kill(d2);
3431                 }
3432                 d3->next = NULL;
3433             }
3434         }
3435 
3436         else                    /* not a list of displays, but a message. handle separately. */
3437             ret = ns_parse_screen_msg(screen, p);
3438 
3439         FREE(p);                /* release our (modified) copy of the status-line */
3440     }
3441 
3442     return ret;
3443 }
3444 
3445 #endif
3446 
3447 
3448 
3449 /****************************************************************************
3450  _____                _                       _
3451 |  ___| __ ___  _ __ | |_       ___ _ __   __| |
3452 | |_ | '__/ _ \| '_ \| __|____ / _ \ '_ \ / _` |
3453 |  _|| | | (_) | | | | ||_____|  __/ | | | (_| |
3454 |_|  |_|  \___/|_| |_|\__|     \___|_| |_|\__,_|
3455 
3456 
3457 frontend abstraction (callbacks for messages to the client)
3458 
3459    this abstracts the frontend against the backend; the abstraction-layer
3460    (libscream) calls these in response to message from the backend (screen,
3461    or whatever) without really knowing what terminal-emulator (Eterm,
3462    konsole, multi-gnome-terminal, ...) the frontend is. */
3463 
3464 
3465 
3466 /* function that moves horizontal scrollbar to x/1000 % of width */
3467 void
ns_register_ssx(_ns_efuns * efuns,int (* set_scroll_x)(void *,int))3468 ns_register_ssx(_ns_efuns * efuns, int (*set_scroll_x) (void *, int))
3469 {
3470     efuns->set_scroll_x = set_scroll_x;
3471 }
3472 
3473 /* function that moves vertical scrollbar to y/1000 % of height */
3474 void
ns_register_ssy(_ns_efuns * efuns,int (* set_scroll_y)(void *,int))3475 ns_register_ssy(_ns_efuns * efuns, int (*set_scroll_y) (void *, int))
3476 {
3477     efuns->set_scroll_y = set_scroll_y;
3478 }
3479 
3480 /* function that sets horizontal scrollbar to w/1000 % of width */
3481 void
ns_register_ssw(_ns_efuns * efuns,int (* set_scroll_w)(void *,int))3482 ns_register_ssw(_ns_efuns * efuns, int (*set_scroll_w) (void *, int))
3483 {
3484     efuns->set_scroll_w = set_scroll_w;
3485 }
3486 
3487 /* function that sets vertical scrollbar to h/1000 % of height */
3488 void
ns_register_ssh(_ns_efuns * efuns,int (* set_scroll_h)(void *,int))3489 ns_register_ssh(_ns_efuns * efuns, int (*set_scroll_h) (void *, int))
3490 {
3491     efuns->set_scroll_h = set_scroll_h;
3492 }
3493 
3494 /* function that redraws the terminal */
3495 void
ns_register_red(_ns_efuns * efuns,int (* redraw)(void *))3496 ns_register_red(_ns_efuns * efuns, int (*redraw) (void *))
3497 {
3498     efuns->redraw = redraw;
3499 }
3500 
3501 
3502 /* function that redraw part of the terminal */
3503 void
ns_register_rda(_ns_efuns * efuns,int (* redraw_xywh)(void *,int,int,int,int))3504 ns_register_rda(_ns_efuns * efuns, int (*redraw_xywh) (void *, int, int, int, int))
3505 {
3506     efuns->redraw_xywh = redraw_xywh;
3507 }
3508 
3509 /* function that expires buttons */
3510 void
ns_register_exb(_ns_efuns * efuns,int (* expire_buttons)(void *,int))3511 ns_register_exb(_ns_efuns * efuns, int (*expire_buttons) (void *, int))
3512 {
3513     efuns->expire_buttons = expire_buttons;
3514 }
3515 
3516 /* function to call when a new client was added ("add tab").
3517    after denotes the index of the button after which this one should
3518    be inserted (0..n, 0 denoting "make it the first button") */
3519 void
ns_register_ins(_ns_efuns * efuns,int (* ins_disp)(void *,int,int,char *))3520 ns_register_ins(_ns_efuns * efuns, int (*ins_disp) (void *, int, int, char *))
3521 {
3522     efuns->ins_disp = ins_disp;
3523 }
3524 
3525 /* function to call when a client was closed ("remove tab") */
3526 void
ns_register_del(_ns_efuns * efuns,int (* del_disp)(void *,int))3527 ns_register_del(_ns_efuns * efuns, int (*del_disp) (void *, int))
3528 {
3529     efuns->del_disp = del_disp;
3530 }
3531 
3532 /* function to call when a client's title was changed ("update tab") */
3533 void
ns_register_upd(_ns_efuns * efuns,int (* upd_disp)(void *,int,int,char *))3534 ns_register_upd(_ns_efuns * efuns, int (*upd_disp) (void *, int, int, char *))
3535 {
3536     efuns->upd_disp = upd_disp;
3537 }
3538 
3539 /* function to pass status lines to */
3540 void
ns_register_err(_ns_efuns * efuns,int (* err_msg)(void *,int,char *))3541 ns_register_err(_ns_efuns * efuns, int (*err_msg) (void *, int, char *))
3542 {
3543     efuns->err_msg = err_msg;
3544 }
3545 
3546 /* function that will execute client programs (in pseudo-terminal et al) */
3547 void
ns_register_exe(_ns_efuns * efuns,int (* execute)(void *,char **))3548 ns_register_exe(_ns_efuns * efuns, int (*execute) (void *, char **))
3549 {
3550     efuns->execute = execute;
3551 }
3552 
3553 /* function that will hand text as input to the client */
3554 void
ns_register_txt(_ns_efuns * efuns,int (* inp_text)(void *,int,char *))3555 ns_register_txt(_ns_efuns * efuns, int (*inp_text) (void *, int, char *))
3556 {
3557     efuns->inp_text = inp_text;
3558 }
3559 
3560 
3561 
3562 /* function that will open a dialog */
3563 void
ns_register_inp(_ns_efuns * efuns,int (* inp_dial)(void *,char *,int,char **,int (*)(void *,char *,size_t,size_t)))3564 ns_register_inp(_ns_efuns * efuns, int (*inp_dial) (void *, char *, int, char **, int (*)(void *, char *, size_t, size_t)))
3565 {
3566     efuns->inp_dial = inp_dial;
3567 }
3568 
3569 
3570 
3571 /* function that will handle tab-completion in a dialog */
3572 void
ns_register_tab(_ns_efuns * efuns,int (* inp_tab)(void *,char * [],int,char *,size_t,size_t))3573 ns_register_tab(_ns_efuns * efuns, int (*inp_tab) (void *, char *[], int, char *, size_t, size_t))
3574 {
3575     efuns->inp_tab = inp_tab;
3576 }
3577 
3578 
3579 
3580 /* function that will do whatever while waiting */
3581 void
ns_register_fun(_ns_efuns * efuns,int (* inp_fun)(void *,int))3582 ns_register_fun(_ns_efuns * efuns, int (*inp_fun) (void *, int))
3583 {
3584     efuns->waitstate = inp_fun;
3585 }
3586 
3587 
3588 
3589 /* get callbacks.  at least one of session and display must be non-NULL.
3590    s  session, or NULL. if NULL, will be initialized from d->sess
3591    d  display, or NULL. if NULL, will be initialized from s->curr.
3592                         if set, will override session callbacks;
3593                         note that NULL pointers in d->efuns *will*
3594                         override (disable) non-NULL pointers in s->efuns!
3595    <- callback-struct */
3596 
3597 _ns_efuns *
ns_get_efuns(_ns_sess * s,_ns_disp * d)3598 ns_get_efuns(_ns_sess * s, _ns_disp * d)
3599 {
3600     if (!s) {
3601         if (!d || !d->sess)
3602             return NULL;
3603         else
3604             s = d->sess;
3605     }
3606     if (!d)
3607         d = s->curr;
3608     if (d && d->efuns)
3609         return d->efuns;
3610     else
3611         return s->efuns;
3612 }
3613 
3614 
3615 
3616 /* ns_inp_dial
3617    open a dialog (wrapp around efuns->inp_dial)
3618    s        the session
3619   !retstr   where we'll store a pointer to the result (the user's input)
3620    prompt   the prompt to appear in the dialog box
3621    <-       msg */
3622 
3623 
3624 int
ns_inp_dial(_ns_sess * s,char * prompt,int maxlen,char ** retstr,int (* inp_tab)(void *,char *,size_t,size_t))3625 ns_inp_dial(_ns_sess * s, char *prompt, int maxlen, char **retstr, int (*inp_tab) (void *, char *, size_t, size_t))
3626 {
3627     _ns_efuns *efuns;
3628     int ret = NS_SUCC;
3629 
3630     if (NS_EFUN_EXISTS(efuns, s, NULL, inp_dial)) {
3631         (void) efuns->inp_dial((void *) s, prompt, maxlen, retstr, inp_tab);
3632     } else {
3633         ret = NS_EFUN_NOT_SET;
3634         D_ESCREEN(("ns_inp_dial: sess->efuns->inp_dial not set!\n"));
3635     }
3636     return ret;
3637 }
3638 
3639 
3640 
3641 /***************************************************************************/
3642