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