1 /*
2 * screen.c
3 *
4 * Written By Matthew Green, based on portions of window.c
5 * by Michael Sandrof.
6 *
7 * Copyright (c) 1990 Michael Sandrof.
8 * Copyright (c) 1993-2017 Matthew R. Green.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include "irc.h"
36 IRCII_RCSID("@(#)$eterna: screen.c,v 1.209 2017/07/02 14:33:19 mrg Exp $");
37
38 #ifdef HAVE_SYS_UN_H
39 # include <sys/un.h>
40 #endif /* HAVE_SYS_UN_H */
41
42 #ifdef HAVE_ICONV_H
43 # include <iconv.h>
44 #endif /* HAVE ICONV_H */
45
46 #include "screen.h"
47 #include "menu.h"
48 #include "window.h"
49 #include "output.h"
50 #include "vars.h"
51 #include "server.h"
52 #include "list.h"
53 #include "ircterm.h"
54 #include "names.h"
55 #include "ircaux.h"
56 #include "input.h"
57 #include "log.h"
58 #include "hook.h"
59 #include "dcc.h"
60 #include "translat.h"
61 #include "exec.h"
62 #include "newio.h"
63 #include "parse.h"
64 #include "edit.h"
65 #include "strsep.h"
66
67 #ifdef lines
68 #undef lines
69 #undef columns
70 #endif /* lines */
71
72 struct screen_stru
73 {
74 int screennum;
75 Window *current_window;
76 unsigned int last_window_refnum; /* reference number of the
77 window that was last
78 the current_window */
79 Window *window_list; /* List of all visible
80 windows */
81 Window *window_list_end; /* end of visible window
82 list */
83 Window *cursor_window; /* Last window to have
84 something written to it */
85 int visible_windows; /* total number of windows */
86 WindowStack *window_stack; /* the windows here */
87
88 EditInfo *edit_info; /* our edit info */
89
90 Screen *next; /* the Screen list */
91
92 int fdin; /* These are the file */
93 FILE *fpout; /* pointers and descriptions */
94 int fdout; /* input/output */
95 int wserv_fd; /* control socket for wserv */
96
97 Prompt *promptlist;
98
99 u_char *redirect_name;
100 u_char *redirect_token;
101 int redirect_server;
102
103 u_char *tty_name;
104 int co, li;
105
106 int old_term_co, old_term_li;
107
108 ScreenInputData *inputdata;
109
110 int alive;
111 };
112
113 static int screen_get_fdout(Screen *);
114 static void screen_set_fdin(Screen *, int);
115 static void screen_set_fpout(Screen *, FILE *);
116 static void screen_set_wserv_fd(Screen *, int);
117 static void screen_set_fdout(Screen *, int);
118
119 /* are we currently processing a redirect command? */
120 static int current_in_redirect;
121
122 static Window *to_window;
123 static Screen *current_screen;
124 static Screen *main_screen;
125 static Screen *last_input_screen;
126
127 /* Our list of screens */
128 static Screen *screen_list = NULL;
129
130 /*
131 * create_new_screen creates a new screen structure. with the help of
132 * this structure we maintain ircII windows that cross screen window
133 * boundaries.
134 */
135 Screen *
create_new_screen(void)136 create_new_screen(void)
137 {
138 Screen *new = NULL,
139 **list;
140 static int refnumber = 0;
141
142 for (list = &screen_list; *list; list = &((*list)->next))
143 {
144 if (!screen_get_alive(*list))
145 {
146 new = *list;
147 break;
148 }
149 }
150 if (!new)
151 {
152 new = new_malloc(sizeof *new);
153 new->inputdata = NULL;
154 new->screennum = ++refnumber;
155 new->next = screen_list;
156 screen_list = new;
157 }
158 new->last_window_refnum = 1;
159 new->window_list = NULL;
160 new->window_list_end = NULL;
161 new->cursor_window = NULL;
162 new->current_window = NULL;
163 new->visible_windows = 0;
164 new->window_stack = NULL;
165 new->edit_info = alloc_edit_info();
166 screen_set_fdout(new, 1);
167 screen_set_fpout(new, stdout);
168 screen_set_fdin(new, 0);
169 new->alive = 1;
170 new->promptlist = NULL;
171 new->redirect_name = NULL;
172 new->redirect_token = NULL;
173 new->tty_name = NULL;
174 new->li = main_screen ? main_screen->li : 24;
175 new->co = main_screen ? main_screen->co : 79;
176 new->old_term_li = -1;
177 new->old_term_co = -1;
178
179 input_reset_screen(new);
180
181 new->redirect_server = -1;
182 last_input_screen = new;
183 return new;
184 }
185
186 void
set_current_screen(Screen * screen)187 set_current_screen(Screen *screen)
188 {
189 if (screen_get_alive(screen))
190 current_screen = screen;
191 else
192 current_screen = screen_list;
193 term_set_fp(screen_get_fpout(current_screen));
194 }
195
196 Screen *
get_current_screen(void)197 get_current_screen(void)
198 {
199 return current_screen;
200 }
201
202 /*
203 * window_redirect: Setting who to non null will cause IRCII to echo all
204 * commands and output from that server (including output generated by IRCII)
205 * to who. Setting who to null disables this
206 *
207 * XXXMRG why does this operate on the whole screen instead of a window?
208 */
209 void
window_redirect(u_char * who,int server)210 window_redirect(u_char *who, int server)
211 {
212 if (who)
213 malloc_snprintf(¤t_screen->redirect_token, "%04d%s", server, who);
214 else
215 malloc_snprintf(¤t_screen->redirect_token, "%04d#LAME", server);
216 malloc_strcpy(¤t_screen->redirect_name, who);
217 current_screen->redirect_server = server;
218 }
219
220 int
check_screen_redirect(u_char * nick)221 check_screen_redirect(u_char *nick)
222 {
223 Screen *screen,
224 *tmp_screen;
225
226 for (screen = screen_first(); screen; screen = screen->next)
227 {
228 if (!screen->redirect_token)
229 continue;
230 if (!my_strcmp(nick, screen->redirect_token))
231 {
232 tmp_screen = current_screen;
233 set_current_screen(screen);
234 window_redirect(NULL, get_from_server());
235 set_current_screen(tmp_screen);
236 return 1;
237 }
238 }
239 return 0;
240 }
241
242 /* Old screens never die. They just fade away. */
243
244 #ifdef WINDOW_CREATE
245 void
kill_screen(Screen * screen)246 kill_screen(Screen *screen)
247 {
248 Window *window;
249
250 if (main_screen == screen)
251 {
252 say("You may not kill the main screen");
253 return;
254 }
255 if (screen_get_fdin(screen))
256 {
257 new_close(screen_get_fdin(screen));
258 new_close(screen_get_fdout(screen));
259 /* But fdout is attached to fpout, so close that too.. */
260 fclose(screen_get_fpout(screen));
261 }
262 while ((window = screen_get_window_list(screen)))
263 {
264 screen->window_list = window_get_next(window);
265 add_to_invisible_list(window);
266 }
267
268 if (last_input_screen == screen)
269 last_input_screen = screen_first();
270 screen->alive = 0;
271 }
272
273 int
is_main_screen(Screen * screen)274 is_main_screen(Screen *screen)
275 {
276 return (screen == main_screen);
277 }
278
279 #else
280
281 int
is_main_screen(Screen * screen)282 is_main_screen(Screen *screen)
283 {
284 return 1;
285 }
286
287 #endif /* WINDOW_CREATE */
288
289
290 /*
291 * cursor_not_in_display: This forces the cursor out of the display by
292 * setting the cursor window to null. This doesn't actually change the
293 * physical position of the cursor, but it will force rite() to reset the
294 * cursor upon its next call
295 */
296 void
cursor_not_in_display(void)297 cursor_not_in_display(void)
298 {
299 Debug(DB_CURSOR, "screen %d cursor_window set to NULL",
300 screen_get_screennum(current_screen));
301 set_cursor_window(NULL);
302 }
303
304 /*
305 * cursor_in_display: this forces the cursor_window to be the
306 * current_screen->current_window.
307 * It is actually only used in hold.c to trick the system into thinking the
308 * cursor is in a window, thus letting the input updating routines move the
309 * cursor down to the input line. Dumb dumb dumb
310 */
311 void
cursor_in_display(void)312 cursor_in_display(void)
313 {
314 Debug(DB_CURSOR, "screen %d cursor_window set to window %d (%d)",
315 screen_get_screennum(current_screen), window_get_refnum(curr_scr_win),
316 screen_get_screennum(window_get_screen(curr_scr_win)));
317 set_cursor_window(curr_scr_win);
318 }
319
320 /*
321 * is_cursor_in_display: returns true if the cursor is in one of the windows
322 * (cursor_window is not null), false otherwise
323 */
324 int
is_cursor_in_display(void)325 is_cursor_in_display(void)
326 {
327 if (get_cursor_window())
328 return (1);
329 else
330 return (0);
331 }
332
333 void
decode_colour(u_char ** ptr,int * fg,int * bg)334 decode_colour(u_char **ptr, int *fg, int *bg)
335 {
336 char c;
337 /* At input, *ptr points to the first char after ^C */
338
339 *fg = *bg = 16; /* both unset */
340
341 /*
342 * mIRC doesn't accept codes beginning with comma (,),
343 * VIRC accepts. mIRC is out authority in this case.
344 */
345 c = **ptr;
346 if (c < '0' || c > '9')
347 {
348 --*ptr;
349 return;
350 }
351
352 *fg = c - '0';
353
354 c = (*ptr)[1];
355 if (c >= '0' && c <= '9')
356 {
357 ++*ptr;
358 *fg = *fg * 10 + c - '0';
359 c = (*ptr)[1];
360 }
361 if (c == ',')
362 {
363 /*
364 * mIRC doesn't eat the comma if it is not followed by
365 * a number, VIRC does. mIRC is again the authority.
366 */
367 c = (*ptr)[2];
368 if (c >= '0' && c <= '9')
369 {
370 *bg = c - '0';
371 *ptr += 2;
372 c = (*ptr)[1];
373 if (c >= '0' && c <= '9')
374 {
375 ++*ptr;
376 *bg = *bg * 10 + c - '0';
377 }
378 }
379 }
380 /* At this point, *ptr points to last char WITHIN the colour code */
381 }
382
383 /* strlen() with internal format codes stripped */
384 /* Return value: number of columns this string takes */
385 int
my_strlen_i(u_char * c)386 my_strlen_i(u_char *c)
387 {
388 int result = 0;
389 struct mb_data mbdata;
390 #ifdef HAVE_ICONV_OPEN
391 mbdata_init(&mbdata, CP(current_irc_encoding()));
392 #else
393 mbdata_init(&mbdata, NULL);
394 #endif /* HAVE_ICONV_OPEN */
395
396 while (*c)
397 {
398 if (*c == REV_TOG || *c == UND_TOG || *c == BOLD_TOG || *c == ALL_OFF)
399 {
400 /* These don't add visual length */
401 ++c;
402 continue;
403 }
404 if (*c == COLOUR_TAG)
405 {
406 /* Colour code didn't add visual length */
407 c += 3;
408 continue;
409 }
410
411 /* Anything else is character data. */
412 decode_mb(c, NULL, 0, &mbdata);
413 result += mbdata.num_columns;
414 c += mbdata.input_bytes;
415 }
416
417 mbdata_done(&mbdata);
418 return result;
419 }
420
421 /* strlen() with colour codes stripped */
422 /* Return value: Number of columns this string takes */
423 int
my_strlen_c(u_char * c)424 my_strlen_c(u_char *c)
425 {
426 int result = 0;
427 struct mb_data mbdata;
428 #ifdef HAVE_ICONV_OPEN
429 mbdata_init(&mbdata, CP(current_irc_encoding()));
430 #else
431 mbdata_init(&mbdata, NULL);
432 #endif /* HAVE_ICONV_OPEN */
433
434 while (*c)
435 {
436 if (*c == REV_TOG || *c == UND_TOG || *c == BOLD_TOG || *c == ALL_OFF)
437 {
438 /* These don't add visual length */
439 ++c;
440 continue;
441 }
442 if (*c == COLOUR_TAG)
443 {
444 int fg, bg;
445
446 ++c;
447 decode_colour(&c, &fg, &bg);
448 ++c;
449 /* Colour code didn't add visual length */
450 continue;
451 }
452
453 /* Anything else is character data. */
454 decode_mb(c, NULL, 0, &mbdata);
455 result += mbdata.num_columns;
456 c += mbdata.input_bytes;
457 }
458 mbdata_done(&mbdata);
459 return result;
460 }
461
462 /* strlen() with colour codes converted to internal codes */
463 /* Doesn't do actual conversion */
464 /* Return value: Number of bytes this string takes when converted */
465 int
my_strlen_ci(u_char * c)466 my_strlen_ci(u_char *c)
467 {
468 int result = 0;
469
470 struct mb_data mbdata;
471 #ifdef HAVE_ICONV_OPEN
472 mbdata_init(&mbdata, CP(current_irc_encoding()));
473 #else
474 mbdata_init(&mbdata, NULL);
475 #endif /* HAVE_ICONV_OPEN */
476
477 while (*c)
478 {
479 if (*c == REV_TOG || *c == UND_TOG || *c == BOLD_TOG || *c == ALL_OFF)
480 {
481 /* These are preserved */
482 ++c;
483 ++result;
484 continue;
485 }
486 if (*c == COLOUR_TAG)
487 {
488 int fg, bg;
489
490 ++c;
491 decode_colour(&c, &fg, &bg);
492 ++c;
493 /* Modifies into three characters */
494 result += 3;
495 continue;
496 }
497
498 /* Anything else is character data. */
499 decode_mb(c, NULL, 0, &mbdata);
500 result += mbdata.output_bytes;
501 c += mbdata.input_bytes;
502 }
503 mbdata_done(&mbdata);
504 return result;
505 }
506
507 /* strcpy() with colour codes converted to internal codes */
508 /* Converts the codes */
509 void
my_strcpy_ci(u_char * dest,size_t destlen,u_char * c)510 my_strcpy_ci(u_char *dest, size_t destlen, u_char *c)
511 {
512 struct mb_data mbdata;
513 #ifdef HAVE_ICONV_OPEN
514 mbdata_init(&mbdata, CP(current_irc_encoding()));
515 #else
516 mbdata_init(&mbdata, NULL);
517 #endif /* HAVE_ICONV_OPEN */
518
519 while (*c && destlen > 1)
520 {
521 if (*c == REV_TOG || *c == UND_TOG || *c == BOLD_TOG || *c == ALL_OFF)
522 {
523 *dest++ = *c++;
524 destlen--;
525 continue;
526 }
527 if (*c == COLOUR_TAG)
528 {
529 int fg, bg;
530
531 if (destlen <= 3)
532 break;
533 *dest++ = *c++;
534 decode_colour(&c, &fg, &bg);
535 *dest++ = fg+1;
536 *dest++ = bg+1;
537 ++c;
538 destlen -= 3;
539 continue;
540 }
541
542 /* Anything else is character data. */
543 decode_mb(c, dest, destlen, &mbdata);
544
545 dest += mbdata.output_bytes;
546 destlen -= mbdata.output_bytes;
547 c += mbdata.input_bytes;
548 }
549 mbdata_done(&mbdata);
550 *dest++ = '\0';
551 }
552
553 int
in_redirect(void)554 in_redirect(void)
555 {
556 return current_in_redirect;
557 }
558
559 /*
560 * add_to_screen: This adds the given null terminated buffer to the screen.
561 * That is, it determines which window the information should go to, which
562 * lastlog the information should be added to, which log the information
563 * should be sent to, etc.
564 *
565 * All logging to files is performed here as well.
566 */
567 void
add_to_screen(u_char * incoming)568 add_to_screen(u_char *incoming)
569 {
570 Win_Trav wt;
571 Window *tmp;
572 u_char *who_from = current_who_from();
573 const int from_server = get_from_server();
574
575 /* eek! */
576 if (!current_screen)
577 {
578 puts(CP(incoming));
579 fflush(stdout);
580 add_to_log(NULL, incoming);
581 return;
582 }
583 /* Handles output redirection first */
584 if (!current_in_redirect && current_screen->redirect_name &&
585 from_server == current_screen->redirect_server)
586 {
587 int i;
588
589 /*
590 * More general stuff by Bisqwit, allow
591 * multiple target redirection (with different targets)
592 */
593 current_in_redirect = 1;
594 i = set_in_on_who(0); /* To allow redirecting /who, /whois and /join */
595 redirect_msg(current_screen->redirect_name, incoming);
596 set_in_on_who(i);
597 current_in_redirect = 0;
598 }
599 if (term_basic())
600 {
601 /* FIXME: Do iconv for "incoming" in dumb mode too */
602 (void)add_to_lastlog(curr_scr_win, incoming);
603 if (do_hook(WINDOW_LIST, "%u %s",
604 window_get_refnum(curr_scr_win), incoming))
605 {
606 add_to_log(NULL, incoming);
607 puts(CP(incoming));
608 }
609 term_flush();
610 return;
611 }
612 if (current_who_level() == LOG_CURRENT &&
613 window_get_server(curr_scr_win) == from_server)
614 {
615 add_to_window(curr_scr_win, incoming);
616 return;
617 }
618 if (to_window)
619 {
620 add_to_window(to_window, incoming);
621 return;
622 }
623 /*
624 * XXX - hack alert
625 * If /echo or /xecho set the message_from, use it here before we
626 * look at who_from. If not, look at it afterwards.
627 * XXX we could probably use to_window to fix this less hackily?
628 */
629 if (current_who_level() && who_level_before_who_from())
630 {
631 wt.init = 1;
632 while ((tmp = window_traverse(&wt)) != NULL)
633 {
634 if ((from_server == window_get_server(tmp) ||
635 from_server == -1) &&
636 (current_who_level() & window_get_window_level(tmp)))
637 {
638 add_to_window(tmp, incoming);
639 return;
640 }
641 }
642 }
643 if (who_from)
644 {
645 if (is_channel(who_from))
646 {
647 Window *window;
648
649 if ((window = channel_window(who_from, from_server)) != NULL)
650 {
651 add_to_window(window, incoming);
652 return;
653 }
654 if (current_who_level() == LOG_DCC)
655 {
656 u_char *buffer = NULL;
657
658 malloc_snprintf(&buffer, "=%s", who_from);
659 window = channel_window(buffer, from_server);
660 new_free(&buffer);
661 if (window != NULL)
662 {
663 add_to_window(window, incoming);
664 return;
665 }
666 }
667 }
668 else
669 {
670 wt.init = 1;
671 while ((tmp = window_traverse(&wt)) != NULL)
672 {
673 u_char *nick = window_get_query_nick(tmp);
674
675 if (nick &&
676 (((current_who_level() == LOG_MSG ||
677 current_who_level() == LOG_NOTICE) &&
678 my_stricmp(who_from, nick) == 0 &&
679 from_server == window_get_server(tmp)) ||
680 (current_who_level() == LOG_DCC &&
681 (*nick == '=' || *nick == '@') &&
682 my_stricmp(who_from, nick + 1) == 0)))
683 {
684 add_to_window(tmp, incoming);
685 return;
686 }
687 }
688 wt.init = 1;
689 while ((tmp = window_traverse(&wt)) != NULL)
690 {
691 if (from_server == window_get_server(tmp))
692 {
693 if (window_has_who_from(tmp))
694 {
695 add_to_window(tmp, incoming);
696 return;
697 }
698 }
699 }
700 }
701 }
702 if (current_who_level() && !who_level_before_who_from())
703 {
704 wt.init = 1;
705 while ((tmp = window_traverse(&wt)) != NULL)
706 {
707 if ((from_server == window_get_server(tmp) ||
708 from_server == -1) &&
709 (current_who_level() & window_get_window_level(tmp)))
710 {
711 add_to_window(tmp, incoming);
712 return;
713 }
714 }
715 }
716 if (from_server == window_get_server(curr_scr_win))
717 tmp = curr_scr_win;
718 else
719 {
720 wt.init = 1;
721 while ((tmp = window_traverse(&wt)) != NULL)
722 {
723 if (window_get_server(tmp) == from_server)
724 break;
725 }
726 if (!tmp)
727 tmp = curr_scr_win;
728 }
729 add_to_window(tmp, incoming);
730 }
731
732 void
close_all_screen(void)733 close_all_screen(void)
734 {
735 Screen *screen;
736
737 for (screen = screen_first(); screen && screen != current_screen;
738 screen = screen->next)
739 if (screen_get_alive(screen) && screen_get_fdin(screen) != 0)
740 new_close(screen_get_fdin(screen));
741 }
742
743 #ifdef WINDOW_CREATE
744 Window *
create_additional_screen(int wanted_type)745 create_additional_screen(int wanted_type)
746 {
747 Window *win;
748 Screen *oldscreen;
749 u_char *displayvar,
750 *termvar;
751 int screen_type = ST_NOTHING;
752 struct sockaddr_un sock, error_sock,
753 *sockaddr = &sock,
754 *error_sockaddr = &error_sock,
755 NewSock;
756 socklen_t NsZ;
757 int s, es;
758 int fd;
759 FILE *fp;
760 int wserv_fd;
761 fd_set fd_read;
762 struct timeval time_out;
763 pid_t child;
764 int old_timeout;
765 #define IRCXTERM_MAX 10
766 u_char *ircxterm[IRCXTERM_MAX];
767 u_char *ircxterm_env;
768 u_char *xterm = NULL;
769 u_char *def_xterm = get_string_var(XTERM_PATH_VAR);
770 u_char *p, *q;
771 u_char buffer[BIG_BUFFER_SIZE];
772 pid_t pid = getpid();
773 int ircxterm_num;
774 int i;
775 static int cycle = 0;
776 int mycycle = cycle++;
777
778 #ifdef DAEMON_UID
779 if (DAEMON_UID == getuid())
780 {
781 say("you are not permitted to use WINDOW CREATE");
782 return NULL;
783 }
784 #endif /* DAEMON_UID */
785
786 if (!def_xterm)
787 def_xterm = UP("xterm");
788
789 ircxterm_num = 0;
790 p = ircxterm_env = my_getenv("IRCXTERM");
791 if (p)
792 {
793 q = ircxterm_env + my_strlen(ircxterm_env);
794 while (p < q && ircxterm_num < IRCXTERM_MAX)
795 {
796 while (':' == *p)
797 p++;
798 if ('\0' == *p)
799 break;
800 ircxterm[ircxterm_num++] = p;
801 while (':' != *p && '\0' != *p)
802 p++;
803 if (':' == *p)
804 {
805 *p = '\0';
806 p++;
807 }
808 }
809 }
810 else
811 {
812 ircxterm[ircxterm_num] = def_xterm;
813 ircxterm_num++;
814 }
815
816 /*
817 * Environment variable STY has to be set for screen to work.. so it is
818 * the best way to check screen.. regardless of what TERM is, the
819 * execpl() for screen won't just open a new window if STY isn't set,
820 * it will open a new screen process, and run the wserv in its first
821 * window, not what we want... -phone
822 */
823 if ((wanted_type == ST_NOTHING || wanted_type == ST_SCREEN) &&
824 0 != my_getenv("STY"))
825 screen_type = ST_SCREEN;
826 else if ((wanted_type == ST_NOTHING || wanted_type == ST_XTERM) &&
827 NULL != (displayvar = my_getenv("DISPLAY")) &&
828 NULL != (termvar = my_getenv("TERM")))
829 {
830 screen_type = ST_XTERM;
831 if (0 == my_strncmp(termvar, "sun", 3))
832 {
833 xterm = def_xterm;
834 }
835 else
836 {
837 for (; *termvar; termvar++)
838 {
839 for (i = 0; i < ircxterm_num; i++)
840 {
841 if (!my_strncmp(termvar, ircxterm[i], my_strlen(ircxterm[i])))
842 {
843 xterm = ircxterm[i];
844 termvar = empty_string();
845 break;
846 }
847 }
848 }
849 }
850 if (!xterm)
851 xterm = def_xterm;
852 }
853
854 if (screen_type == ST_NOTHING)
855 {
856 say("I don't know how to create new windows for this terminal");
857 return NULL;
858 }
859 say("Opening new %s...",
860 screen_type == ST_XTERM ? "window" :
861 screen_type == ST_SCREEN ? "screen" :
862 "wound" );
863 snprintf(sock.sun_path, sizeof sock.sun_path, "/tmp/irc_%08d_%x", (int) pid, mycycle);
864 sock.sun_family = AF_UNIX;
865 if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
866 {
867 say("Can't create UNIX socket, punting /WINDOW CREATE");
868 return NULL;
869 }
870 if (bind(s, (struct sockaddr *) &sock, (int)(2 + my_strlen(sock.sun_path))) < 0)
871 {
872 say("Can't bind UNIX socket, punting /WINDOW CREATE");
873 return NULL;
874 }
875 if (listen(s, 1) < 0)
876 {
877 say("Can't bind UNIX socket, punting /WINDOW CREATE");
878 return NULL;
879 }
880 snprintf(error_sock.sun_path, sizeof error_sock.sun_path, "/tmp/irc_error_%08d_%x", (int) pid, mycycle);
881 error_sock.sun_family = AF_UNIX;
882 if ((es = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
883 {
884 say("Can't create UNIX socket, punting /WINDOW CREATE");
885 return NULL;
886 }
887 if (bind(es, (struct sockaddr *) &error_sock, (int)(2 + my_strlen(error_sock.sun_path))) < 0)
888 {
889 say("Can't bind UNIX socket, punting /WINDOW CREATE");
890 return NULL;
891 }
892 if (listen(es, 1) < 0)
893 {
894 say("Can't bind UNIX socket, punting /WINDOW CREATE");
895 return NULL;
896 }
897
898 oldscreen = current_screen;
899 set_current_screen(create_new_screen());
900 if (0 == (child = fork()))
901 {
902 (void)setuid(getuid());
903 (void)setgid(getgid());
904 /*
905 * Unlike most other cases, it is important here to close down
906 * *ALL* unneeded file descriptors. Failure to do so can cause
907 * Things like server and DCC connections to fail to close on
908 * request. This isn't a problem with "screen", but is with X.
909 */
910 new_close(s);
911 new_close(es);
912 close_all_screen();
913 close_all_dcc();
914 close_all_exec();
915 close_all_server();
916 i = 0;
917 if (screen_type == ST_SCREEN)
918 {
919 #define MAX_ARGS 64
920 char *args[MAX_ARGS];
921 u_char *ss,
922 *t,
923 *opts = NULL;
924
925 Debug(DB_WINCREATE, "going to execvp screen wserv...");
926 args[i++] = "screen";
927 if ((ss = get_string_var(SCREEN_OPTIONS_VAR)) != NULL)
928 {
929 malloc_strcpy(&opts, ss);
930 while ((t = next_arg(opts, &opts)) != NULL)
931 {
932 Debug(DB_WINCREATE, "added option `%s'", t);
933 args[i++] = CP(t);
934
935 /* add 6 more below */
936 if (i + 6 > MAX_ARGS) {
937 say("Too many args in SCREEN_OPTIONS_VAR");
938 break;
939 }
940 }
941 }
942 args[i++] = CP(get_string_var(WSERV_PATH_VAR));
943 #if 0
944 args[i++] = "-t";
945 args[i++] = CP(program_name());
946 #endif
947 args[i++] = sockaddr->sun_path;
948 args[i++] = error_sockaddr->sun_path;
949 Debug(DB_WINCREATE, "added: %s %s %s", args[i-3], args[i-2], args[i-1]);
950 args[i++] = NULL;
951 execvp("screen", args);
952 }
953 else if (screen_type == ST_XTERM)
954 {
955 int lines,
956 columns;
957 char *args[64];
958 u_char geom[20],
959 *ss,
960 *t,
961 *opts = NULL;
962
963 Debug(DB_WINCREATE, "going to execvp xterm wserv...");
964 copy_window_size(&lines, &columns);
965 snprintf(CP(geom), sizeof geom, "%dx%d", columns, lines);
966 args[i++] = CP(xterm);
967 Debug(DB_WINCREATE, "xterm name is %s", args[0]);
968 if ((ss = get_string_var(XTERM_GEOMOPTSTR_VAR)) != NULL)
969 args[i++] = CP(ss);
970 else
971 args[i++] = "-geom";
972 args[i++] = CP(geom);
973 Debug(DB_WINCREATE, "added geom: %s %s", args[i-2], args[i-1]);
974 if ((ss = get_string_var(XTERM_OPTIONS_VAR)) != NULL)
975 {
976 malloc_strcpy(&opts, ss);
977 Debug(DB_WINCREATE, "got xterm options: %s", opts);
978 while ((t = next_arg(opts, &opts)) != NULL)
979 {
980 Debug(DB_WINCREATE, "added option `%s'", t);
981 args[i++] = CP(t);
982 }
983 }
984 args[i++] = "-e";
985 args[i++] = CP(get_string_var(WSERV_PATH_VAR));
986 args[i++] = sockaddr->sun_path;
987 args[i++] = error_sockaddr->sun_path;
988 Debug(DB_WINCREATE, "added: %s %s %s %s", args[i-4], args[i-3], args[i-2], args[i-1]);
989 args[i] = NULL;
990 execvp(CP(xterm), args);
991 }
992 perror("execve");
993 unlink(sockaddr->sun_path);
994 unlink(error_sockaddr->sun_path);
995 exit(errno ? errno : -1);
996 }
997 NsZ = sizeof(NewSock);
998 FD_ZERO(&fd_read);
999 FD_SET(s, &fd_read);
1000 FD_SET(es, &fd_read);
1001 time_out.tv_sec = (time_t) 5;
1002 time_out.tv_usec = 0;
1003 sleep(1);
1004
1005 Debug(DB_WINCREATE, "-- forked wserv!");
1006 /* using say(), yell() can be bad in this next section of code. */
1007
1008 switch (select(NFDBITS , &fd_read, NULL, NULL, &time_out))
1009 {
1010 case -1:
1011 case 0:
1012 errno = get_child_exit(child);
1013 new_close(s);
1014 new_close(es);
1015 kill_screen(current_screen);
1016 kill(child, SIGKILL);
1017 last_input_screen = oldscreen;
1018 set_current_screen(oldscreen);
1019 yell("child %s with %d", (errno < 1) ? "signaled" : "exited",
1020 (errno < 1) ? -errno : errno);
1021 win = NULL;
1022 break;
1023 default:
1024 fd = accept(s, (struct sockaddr *) &NewSock, &NsZ);
1025 if (fd < 0)
1026 {
1027 win = NULL;
1028 break;
1029 }
1030 wserv_fd = accept(es, (struct sockaddr *) &NewSock, &NsZ);
1031 if (wserv_fd < 0)
1032 {
1033 close(fd);
1034 win = NULL;
1035 break;
1036 }
1037
1038 fp = fdopen(fd, "r+");
1039 if (!fp) {
1040 close(wserv_fd);
1041 close(fd);
1042 win = NULL;
1043 break;
1044 }
1045
1046 screen_set_fdin(current_screen, fd);
1047 screen_set_fdout(current_screen, fd);
1048 screen_set_fpout(current_screen, fp);
1049 screen_set_wserv_fd(current_screen, wserv_fd);
1050 Debug(DB_WINCREATE, "-- got connected!");
1051
1052 term_set_fp(fp);
1053 new_close(s);
1054 new_close(es);
1055 old_timeout = dgets_timeout(5);
1056 /*
1057 * dgets returns 0 on EOF and -1 on timeout. both of these are
1058 * error conditions in this case, so we bail out here.
1059 */
1060 if (dgets(buffer, sizeof buffer, screen_get_fdin(current_screen)) < 1)
1061 {
1062 new_close(screen_get_fdin(current_screen));
1063 kill_screen(current_screen);
1064 kill(child, SIGKILL);
1065 last_input_screen = oldscreen;
1066 set_current_screen(oldscreen);
1067 (void) dgets_timeout(old_timeout);
1068 win = NULL;
1069 break;
1070 }
1071 else
1072 malloc_strcpy(¤t_screen->tty_name, buffer);
1073 win = new_window();
1074 (void) refresh_screen(0, NULL);
1075 set_current_screen(oldscreen);
1076 (void) dgets_timeout(old_timeout);
1077 }
1078 unlink(sockaddr->sun_path);
1079 unlink(error_sockaddr->sun_path);
1080 return win;
1081 }
1082 #endif /* WINDOW_CREATE */
1083
1084 void
screen_wserv_message(Screen * screen)1085 screen_wserv_message(Screen *screen)
1086 {
1087 u_char buf[128], *rs, *cs, *comma;
1088 int old_timeout, li, co;
1089
1090 old_timeout = dgets_timeout(0);
1091 if (dgets(buf, 128, current_screen->wserv_fd) < 1)
1092 {
1093 /* this should be impossible. */
1094 if (!is_main_screen(screen))
1095 kill_screen(screen);
1096 }
1097 (void) dgets_timeout(old_timeout);
1098 Debug(DB_WINCREATE, "got buf: %s", buf);
1099 /* should have "rows,cols\n" */
1100 rs = buf;
1101 comma = my_index(buf, ',');
1102 if (comma == NULL)
1103 {
1104 yell("--- got wserv message: %s, no comma?", buf);
1105 return;
1106 }
1107 *comma = 0;
1108 cs = comma+1;
1109 comma = my_index(buf, '\n');
1110 if (comma != NULL)
1111 *comma = 0;
1112
1113 li = atoi(CP(rs));
1114 co = atoi(CP(cs));
1115
1116 Debug(DB_WINCREATE, "got li %d[%d] co %d[%d]", li, screen->li, co, screen->co);
1117
1118 if (screen->li != li || screen->co != co)
1119 {
1120 Screen *old_screen;
1121
1122 screen->li = li;
1123 screen->co = co;
1124 old_screen = current_screen;
1125 current_screen = screen;
1126 refresh_screen(0, 0);
1127 current_screen = old_screen;
1128 }
1129 }
1130
1131 int
screen_get_screennum(Screen * screen)1132 screen_get_screennum(Screen *screen)
1133 {
1134 return screen->screennum;
1135 }
1136
1137 Window *
screen_get_current_window(Screen * screen)1138 screen_get_current_window(Screen *screen)
1139 {
1140 return screen->current_window;
1141 }
1142
1143 void
screen_set_current_window(Screen * screen,Window * window)1144 screen_set_current_window(Screen *screen, Window *window)
1145 {
1146 screen->current_window = window;
1147 }
1148
1149 ScreenInputData *
screen_get_inputdata(Screen * screen)1150 screen_get_inputdata(Screen *screen)
1151 {
1152 return screen->inputdata;
1153 }
1154
1155 void
screen_set_inputdata(Screen * screen,ScreenInputData * inputdata)1156 screen_set_inputdata(Screen *screen, ScreenInputData *inputdata)
1157 {
1158 screen->inputdata = inputdata;
1159 }
1160
1161 int
screen_get_alive(Screen * screen)1162 screen_get_alive(Screen *screen)
1163 {
1164 return screen->alive;
1165 }
1166
1167 u_char *
get_redirect_token(void)1168 get_redirect_token(void)
1169 {
1170 return current_screen->redirect_token;
1171 }
1172
1173 int
screen_get_wserv_fd(Screen * screen)1174 screen_get_wserv_fd(Screen *screen)
1175 {
1176 return screen->wserv_fd;
1177 }
1178
1179 Window *
screen_get_window_list(Screen * screen)1180 screen_get_window_list(Screen *screen)
1181 {
1182 return screen->window_list;
1183 }
1184
1185 void
screen_set_window_list(Screen * screen,Window * window)1186 screen_set_window_list(Screen *screen, Window *window)
1187 {
1188 screen->window_list = window;
1189 }
1190
1191 Window *
screen_get_window_list_end(Screen * screen)1192 screen_get_window_list_end(Screen *screen)
1193 {
1194 return screen->window_list_end;
1195 }
1196
1197 void
screen_set_window_list_end(Screen * screen,Window * window)1198 screen_set_window_list_end(Screen *screen, Window *window)
1199 {
1200 screen->window_list_end = window;
1201 }
1202
1203 int
screen_get_visible_windows(Screen * screen)1204 screen_get_visible_windows(Screen *screen)
1205 {
1206 return screen->visible_windows;
1207 }
1208
1209 void
incr_visible_windows(void)1210 incr_visible_windows(void)
1211 {
1212 current_screen->visible_windows++;
1213 }
1214
1215 void
decr_visible_windows(void)1216 decr_visible_windows(void)
1217 {
1218 current_screen->visible_windows--;
1219 }
1220
1221 int
screen_get_fdin(Screen * screen)1222 screen_get_fdin(Screen *screen)
1223 {
1224 return screen->fdin;
1225 }
1226
1227 static int
screen_get_fdout(Screen * screen)1228 screen_get_fdout(Screen *screen)
1229 {
1230 return screen->fdout;
1231 }
1232
1233 static void
screen_set_fdout(Screen * screen,int fdout)1234 screen_set_fdout(Screen *screen, int fdout)
1235 {
1236 screen->fdout = fdout;
1237 }
1238
1239 static void
screen_set_fdin(Screen * screen,int fdin)1240 screen_set_fdin(Screen *screen, int fdin)
1241 {
1242 screen->fdin = fdin;
1243 }
1244
1245 FILE *
screen_get_fpout(Screen * screen)1246 screen_get_fpout(Screen *screen)
1247 {
1248 return screen->fpout;
1249 }
1250
1251 static void
screen_set_fpout(Screen * screen,FILE * fpout)1252 screen_set_fpout(Screen *screen, FILE *fpout)
1253 {
1254 screen->fpout = fpout;
1255 }
1256
1257 static void
screen_set_wserv_fd(Screen * screen,int wserv_fd)1258 screen_set_wserv_fd(Screen *screen, int wserv_fd)
1259 {
1260 screen->wserv_fd = wserv_fd;
1261 }
1262
1263 unsigned
get_last_window_refnum(void)1264 get_last_window_refnum(void)
1265 {
1266 return current_screen->last_window_refnum;
1267 }
1268
1269 void
set_last_window_refnum(unsigned refnum)1270 set_last_window_refnum(unsigned refnum)
1271 {
1272 current_screen->last_window_refnum = refnum;
1273 }
1274
1275 Prompt *
get_current_promptlist(void)1276 get_current_promptlist(void)
1277 {
1278 return current_screen->promptlist;
1279 }
1280
1281 void
set_current_promptlist(Prompt * promptlist)1282 set_current_promptlist(Prompt *promptlist)
1283 {
1284 current_screen->promptlist = promptlist;
1285 }
1286
1287 Screen *
screen_get_next(Screen * screen)1288 screen_get_next(Screen *screen)
1289 {
1290 return screen->next;
1291 }
1292
1293 WindowStack *
get_window_stack(void)1294 get_window_stack(void)
1295 {
1296 return current_screen->window_stack;
1297 }
1298
1299 void
set_window_stack(WindowStack * window_stack)1300 set_window_stack(WindowStack *window_stack)
1301 {
1302 current_screen->window_stack = window_stack;
1303 }
1304
1305 Window *
get_cursor_window(void)1306 get_cursor_window(void)
1307 {
1308 return current_screen->cursor_window;
1309 }
1310
1311 void
set_cursor_window(Window * cursor_window)1312 set_cursor_window(Window *cursor_window)
1313 {
1314 current_screen->cursor_window = cursor_window;
1315 }
1316
1317 EditInfo *
get_edit_info(void)1318 get_edit_info(void)
1319 {
1320 return current_screen->edit_info;
1321 }
1322
1323 int
screen_set_size(int new_li,int new_co)1324 screen_set_size(int new_li, int new_co)
1325 {
1326 int rv = 0;
1327
1328 if (current_screen->li != new_li ||
1329 current_screen->co != new_co)
1330 rv = 1;
1331
1332 current_screen->old_term_li = current_screen->li;
1333 current_screen->old_term_co = current_screen->co;
1334 current_screen->li = new_li;
1335 current_screen->co = new_co;
1336
1337 Debug(DB_WINDOW, "new lines/colums = %d/%d (old = %d/%d)",
1338 new_li, new_co,
1339 current_screen->old_term_li,
1340 current_screen->old_term_co);
1341 return rv;
1342 }
1343
1344 int
screen_get_old_co(void)1345 screen_get_old_co(void)
1346 {
1347 return current_screen->old_term_co;
1348 }
1349
1350 int
screen_get_old_li(void)1351 screen_get_old_li(void)
1352 {
1353 return current_screen->old_term_li;
1354 }
1355
1356 int
get_co(void)1357 get_co(void)
1358 {
1359 return current_screen->co;
1360 }
1361
1362 int
get_li(void)1363 get_li(void)
1364 {
1365 return current_screen->li;
1366 }
1367
1368 Screen *
screen_first(void)1369 screen_first(void)
1370 {
1371 return screen_list;
1372 }
1373
1374 void
set_main_screen(Screen * screen)1375 set_main_screen(Screen *screen)
1376 {
1377 main_screen = screen;
1378 }
1379
1380 Screen *
get_last_input_screen(void)1381 get_last_input_screen(void)
1382 {
1383 return last_input_screen;
1384 }
1385
1386 void
set_last_input_screen(Screen * screen)1387 set_last_input_screen(Screen *screen)
1388 {
1389 last_input_screen = screen;
1390 }
1391
1392 Window *
get_to_window(void)1393 get_to_window(void)
1394 {
1395 return to_window;
1396 }
1397
1398 void
set_to_window(Window * window)1399 set_to_window(Window *window)
1400 {
1401 to_window = window;
1402 }
1403