diff --git a/ChangeLog b/ChangeLog index cdcba00..f3a7267 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4643,3 +4643,7 @@ Tue May 7 10:16:56 2002 Michael Jennings (mej) configure.in and configure.ac should no longer differ, so let's not maintain both. ---------------------------------------------------------------------- +Mon May 13 18:46:06 2002 Michael Jennings (mej) + +Latest Escreen patch from Azundris . +---------------------------------------------------------------------- diff --git a/doc/Eterm.1.in b/doc/Eterm.1.in index 365a80b..ce136da 100644 --- a/doc/Eterm.1.in +++ b/doc/Eterm.1.in @@ -621,6 +621,21 @@ Pick up a "screen" session at rather than a local one. URLs look like so (screen://user@host.dom/options), with all parts optional, defaulting to current user at localhost, respectively. Forces Escreen mode, overrides \-\-exec. +.TP +.BI \-Z " lclport:fw:fwport,delay" ", \-\-fw " lclport:fw:fwport,delay +The URL given to \-U is in an intranet behind firewall +.I fw +so we'll build an SSH-tunnel to that firewall (to port 22/SSH, or +.I fwport +if given) from our local machine (using any available port-number, or +.I lclport +if given). Then, after +.I delay +seconds (or a sensible default if not given), we will try to open a +screen session on the host behind the firewall using +.I ssh -p localport ... localhost screen +cf. +.I ssh -L .SH THEMES diff --git a/src/README.Escreen b/src/README.Escreen index 0bc8fc4..a1cee8d 100644 --- a/src/README.Escreen +++ b/src/README.Escreen @@ -21,23 +21,16 @@ there can be no newbie support at this time. 2 - How do I get it? -You already have all you need. Now, compile with ESCREEN defined in -the DEFS in src/Makefile. - - If you don't know how to do that, sorry, but you're not Escreen's - intended audience at this point; please hold on. - +You already have it. ./configure --enable-escreen && make && make install Oh, and you also need the screen program. In our tests, we used -$ screen -version -Screen version 3.09.11 (FAU) 14-Feb-02 +Screen version 3.09.11 (FAU) 14-Feb-02. 3 - How do I start it? If you call it an Eterm, it will behave like one. Same for an Escreen. -(ln -s Eterm Escreen; ./Escreen). - +(you'd have to symlink from Escreen to Eterm first, of course). Additionally, when called with a command-line (-e "/some/program"), the suite will *always* behave like an Eterm, even if invoked as Escreen. Likewise, when called with an URL (-U "screen://user@some.host.gov/"), @@ -49,13 +42,41 @@ it will behave like an Escreen, even if invoked under another name. An Eterm should open with a screen (without a status-line) running inside of it. There should be a button-bar in the Eterm enumerating -the multiplexed terminal (you usually start with one). If you normally -don't use a button-bar, Escreen will create one for you. Otherwise, it -will add buttons to your existing bar. +the multiplexed terminal (you usually start with one), and button with +a menu of screen-related actions. If you normally don't use a button-bar, +Escreen will create one for you. Otherwise, it will add buttons to your +existing bar. -5 - What should I expect in the future? (TODO) +5 - What's new? + +* Support for ".screenrc". This was temporarily disabled to aid in + debugging and has now returned in conjunction with + +* Escreen accepting non-default key setups and + +* renaming of displays + +* sending statements to screen (^A: in the default setup) with + tab-completion + +* support for screen command-line options: + Eterm -U screen://localhost/-xRR+-c~/.screenrc.testing + +* menu for "screen"-functions (if you add your own, use ^A as the + escape-character in the ECHO action; Escreen will remap it to + whatever is actually used in the session) + +* rewrite of key parts for enhanced compatibility + +* alert boxes for messages from screen + +* support for SSH-tunneling through firewalls + + + +6 - What should I expect in the future? (TODO) * Limited support for scrolling. screen doesn't give us all the info we'd need to make this really sexy. There will likely be limited @@ -63,31 +84,37 @@ will add buttons to your existing bar. on remote machine), and possibly support for enhanced screen-clones (like the perchance forthcoming "scream"). -* Support for ".screenrc". This was temporarily disabled to aid in - debugging and will soon return in conjunction with - -* Escreen accepting non-default key setups and - -* menus for "screen"-functions so those unfamiliar with the program - will not need to learn a dozen arcance key-sequences (or even get - used to ^A meaning something new). - 9 - FAQ -Q I don't see any buttons! +Q I don't see any buttons when I connect to a remote screen! -A Debian-users reported this before, and it is presumed to be a problem - with the terminfo-data. Call up Eterm (or xterm, or whatnot). *Then* - call up "screen" from inside that terminal. Is the last line inverted? - If it isn'T, something is seriously weird with your setup, and it's not - a problem in Escreen. Sorry. : ( +A Some Debian-users reported this before, and it is presumed to be a + problem with the terminfo-data. Call up Eterm (or xterm, or whatnot). + *Then* call up "screen" from inside that terminal. Is the last line + inverted? If it isn't, something is seriously weird with your setup, + and it's not a problem in Escreen. Sorry. : ( + 2002/05/13 rewritten code should handle most of these cases Q ^A does funny things! -A Well, yes. ^A talks to the screen program. You will be able to move - that to a key of your choice with the next release. +A Well, yes. ^A talks to the screen program. Put a line + like "escape ^Aa" in "~/.screenrc". The example declares + Control-A (^A) to be the magic key (and ^A-a what you need + to press to send the escape (^A in the example) to the application + instead); you'll want to put something else to move it around. + See "man screen" for details. + 2002/05/13 note that ^A: opens a buffer where you can directly enter + statements like "escape ^Uu"; trying things out will be + easier that way. yes, it will be ^U: afterwards. ; ) + +Q I typed ^A and it *doesn't do* funny things! + +A You probably already have changed the magic escape key to something + other than ^A (on the command-line, within Escreen, or in .screenrc). + Er, you *did* hold Control, then press A, rather then typing a ^ and + an A, right? Q I'd like for the button labels to be in a different colour. @@ -101,15 +128,30 @@ A Escreen cannot run "screen". If you do have "screen", and it is in the search path, screen apparently terminates right away. Normally this means that your screen-sessions are messed up; "screen -ls" will give you a list of them; "screen -wipe" will do the cleaning up. - NOTE: If this happens with a remote session, the remote end might just - find our terminal type confusing. The "--term-name" option with some- - thing vanilla (--term-name vt100) should remedy this. + If this happens with a remote session, the remote end might just find + your terminal type confusing. The "--term-name" option with something + vanilla (--term-name vt100) should remedy this. + +Q Escreen works, except when I use -Z to tunnel through a firewall? + +A Try to manually ssh to the firewall, see how long that takes. Then, + tune the -Z option accordingly (cf man Eterm). Q "screen" support rocks! Can we have it in konsole/multi-gnome-terminal? A I will not attempt anything like that before I am happy with Escreen. - The code, however, is GPL'd, and its use is documented by example of - Eterm. If you can work with that, do. + The code, however, is LGPL'd, and its use is documented by example of + Eterm (grep for ESCREEN). If you can work with that, do. + +Q What's with the licence changing? + +A I wrongly assumed that Eterm was GPL'd rather than BSD'd, so I stuck + a GPL on the first alpha. To correct this mistake, the licence was + changed. In an attempt to honour both the convictions of the authors + of Eterm (BSD licence) and those of screen (GPL) which Escreen tries + to bring together, it appears that the licensing of Escreen should + land somewhere in the middle; therefore, I nominally chose LGPL. + This may be softened to BSD at a later date. Q Who's responsible for this? @@ -120,4 +162,4 @@ A Escreen is an Eterm extension conceived and written by Azundris. you agree that anything happening while or because of using it is your fault, and your fault only. --- Azundris 2002/05/03 http://www.azundris.com/ +-- Azundris 2002/05/13 http://www.azundris.com/ diff --git a/src/actions.c b/src/actions.c index ce91f2c..03dea3e 100644 --- a/src/actions.c +++ b/src/actions.c @@ -59,6 +59,11 @@ action_handle_echo(event_t *ev, action_t *action) { USE_VAR(ev); REQUIRE_RVAL(action->param.string != NULL, 0); +#ifdef ESCREEN + if(TermWin.screen_mode&&TermWin.screen) /* translate escapes */ + ns_screen_command(TermWin.screen,action->param.string); + else +#endif tt_write((unsigned char *) action->param.string, strlen(action->param.string)); return 1; } diff --git a/src/buttons.c b/src/buttons.c index 3e47c01..f47b079 100644 --- a/src/buttons.c +++ b/src/buttons.c @@ -43,6 +43,9 @@ static const char cvs_ident[] = "$Id$"; #include "script.h" #include "term.h" #include "windows.h" +#ifdef ESCREEN +# include "screamcfg.h" +#endif static inline void draw_string(buttonbar_t *, Drawable, GC, int, int, char *, size_t); @@ -226,7 +229,7 @@ bbar_handle_button_press(event_t *ev) } if (bbar->current) { bbar_click_button(bbar, bbar->current); - button_check_action(bbar, bbar->current, 1, ev->xbutton.time); + button_check_action(bbar, bbar->current, ev->xbutton.button, ev->xbutton.time); } return 1; } @@ -768,6 +771,8 @@ bbar_click_button(buttonbar_t *bbar, button_t *button) void button_check_action(buttonbar_t *bbar, button_t *button, unsigned char press, Time t) { + static unsigned char prvs = 0; + switch (button->type) { case ACTION_MENU: if (press) { @@ -781,6 +786,36 @@ button_check_action(buttonbar_t *bbar, button_t *button, unsigned char press, Ti break; case ACTION_ECHO: if (!press) { +#ifdef ESCREEN + if(TermWin.screen_mode&&TermWin.screen) { /* translate escapes */ + if(prvs!=1) { + button_t *b=bbar->buttons; + _ns_disp *d2=TermWin.screen->dsps; + int n=(button->action.string)[1]-'0'; + + while(b&&!(b->flags&NS_SCREAM_CURR)) /* find active disp */ + b=b->next; /* when trying to change name of non- */ + if(b&&b!=button) { /* active display, make that disp active */ + button->flags|=NS_SCREAM_CURR; b->flags&=~NS_SCREAM_CURR; + bbar_draw(bbar, IMAGE_STATE_CURRENT, MODE_MASK); + button->flags&=~NS_SCREAM_CURR; b->flags|=NS_SCREAM_CURR; + + while(d2 && d2->index != n) + d2 = d2->next; + if(d2) + TermWin.screen->curr=d2; /* pre-adjust curr ptr */ + else + fprintf(stderr,NS_PREFIX "button_check_action: no display %d in this session : (\n",n); + (void)ns_screen_command(TermWin.screen,button->action.string); } + + if(prvs==2) /* middle button -- kill */ + (void)ns_parse_screen_key(TermWin.screen,NS_SCREEN_KILL); + else /* right button -- rename */ + (void)ns_parse_screen_key(TermWin.screen,NS_SCREEN_RENAME); } + else /* left button -- select */ + (void)ns_screen_command(TermWin.screen,button->action.string); } + else /* not in screen-mode, use normal facilities */ +#endif tt_write((unsigned char *) button->action.string, strlen(button->action.string)); } break; @@ -792,6 +827,7 @@ button_check_action(buttonbar_t *bbar, button_t *button, unsigned char press, Ti default: break; } + prvs=press; } unsigned char @@ -906,7 +942,7 @@ bbar_draw(buttonbar_t *bbar, unsigned char image_state, unsigned char force_mode gcvalue.foreground = PixColors[button->flags + 2]; gcvalue.font = bbar->font->fid; - if (button->flags && (gc = LIBAST_X_CREATE_GC(GCForeground | GCFont, &gcvalue))) { + if(button->flags&&(gc=LIBAST_X_CREATE_GC(GCForeground | GCFont, &gcvalue))) { draw_string(bbar, bbar->bg, gc, button->text_x, button->text_y, button->text, button->len); XFreeGC(Xdisplay, gc); } else @@ -1015,3 +1051,103 @@ bbar_calc_docked_height(register unsigned char dock_flag) D_BBAR(("Returning %d\n", h)); return h; } + + + +/* redraw a button bar */ +void +bbar_redraw(buttonbar_t *bbar) +{ + bbar_calc_button_sizes(bbar); + bbar_calc_button_positions(bbar); + bbar_draw(bbar, IMAGE_STATE_CURRENT, MODE_MASK); +} + + + +/* insert a button to the given bar, at the given position. + create a new bar if required. + bar the bar + button the button + after insert after this button (-1 for before first button) + addright add to rbuttons instead of buttons + <- NULL, or the bar */ + +buttonbar_t * +bbar_insert_button(buttonbar_t *bbar, button_t *button, int after, int addright) +{ + int state = 1; + + if (!bbar) { + if (!(bbar = bbar_create())) { + fprintf(stderr, "ins_disp: failed to create button-bar...\n"); + return NULL; + } else { + bbar->next=NULL; + bbar_set_font(bbar, "-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1"); + bbar_set_docked(bbar, BBAR_DOCKED_TOP); + } + state = 0; + } + + if(addright) { /* add to rbuttons */ + if (!bbar->rbuttons) { /* first button */ + button->next = NULL; + bbar->rbuttons = button; + } else { + int c=0; + button_t *b = bbar->rbuttons; + do { + c++; + } while((b=b->next)); + if(after>=(c-1)) { + button->next=bbar->rbuttons; + bbar->rbuttons=button; } + else { + b = bbar->rbuttons; + after=c-after-2; + while (after-- > 0 && b->next) + b = b->next; + button->next = b->next; + b->next = button; } + } + } + else { /* add to buttons */ + if (!bbar->buttons || after < 0) { /* first button */ + button->next = bbar->buttons; + bbar->buttons = button; + } else { + button_t *b = bbar->buttons; + while (after-- > 0 && b->next) + b = b->next; + button->next = b->next; + b->next = button; + } + } + + bbar->current = button; + + /* add to list of bbars so bbar_event_init_dispatcher() won't break */ + + if (!state) { + if(buttonbar) { + buttonbar_t *bar=buttonbar; + while(bar->next) + bar=bar->next; + bar->next=bbar; + } + else + buttonbar=bbar; + + bbar_init(bbar, TermWin.width); + bbar_add(bbar); + } + + bbar_redraw(bbar); + + if (!state) { + parent_resize(); + } + + return bbar; +} diff --git a/src/buttons.h b/src/buttons.h index e86ddd3..51d19e7 100644 --- a/src/buttons.h +++ b/src/buttons.h @@ -140,6 +140,8 @@ extern void bbar_draw_all(unsigned char image_state, unsigned char force_modes); extern void bbar_calc_positions(void); extern unsigned long bbar_calc_total_height(void); extern unsigned long bbar_calc_docked_height(unsigned char); +extern void bbar_redraw(buttonbar_t *bbar); +extern buttonbar_t *bbar_insert_button(buttonbar_t *bbar, button_t *button, int after, int addright); _XFUNCPROTOEND diff --git a/src/command.c b/src/command.c index b2bd577..f0dde8a 100644 --- a/src/command.c +++ b/src/command.c @@ -128,6 +128,7 @@ static const char cvs_ident[] = "$Id$"; #endif #include "windows.h" #include "buttons.h" +#include "menus.h" #ifdef ESCREEN # include "screamcfg.h" @@ -2106,7 +2107,7 @@ run_command(char **argv) char **a = argv; if (a) { while (*a) { - puts(*a); + fprintf(stderr, NS_PREFIX "run_command: %s\n",*a); a++; } } @@ -2226,7 +2227,11 @@ run_command(char **argv) my_euid = my_ruid; my_egid = my_rgid; +#ifdef HAVE_USLEEP usleep(10); /* Attempt to force a context switch so that the parent runs before us. */ +#else + sleep(1); /* ugliness */ +#endif D_CMD(("[%d] About to spawn shell\n", getpid())); if (chdir(initial_dir)) { print_warning("Unable to chdir to \"%s\" -- %s\n", initial_dir, strerror(errno)); @@ -2289,55 +2294,61 @@ run_command(char **argv) int set_scroll_x(void *xd, int x) { - printf("set_scroll_x: %d\n", x); - return 0; + fprintf(stderr, NS_PREFIX "set_scroll_x: %d\n", x); + return NS_FAIL; } int set_scroll_y(void *xd, int y) { - printf("set_scroll_y: %d\n", y); - return 0; + fprintf(stderr, NS_PREFIX "set_scroll_y: %d\n", y); + return NS_FAIL; } int set_scroll_w(void *xd, int w) { - printf("set_scroll_w: %d\n", w); - return 0; + fprintf(stderr, NS_PREFIX "set_scroll_w: %d\n", w); + return NS_FAIL; } int set_scroll_h(void *xd, int h) { - printf("set_scroll_h: %d\n", h); - return 0; + fprintf(stderr, NS_PREFIX "set_scroll_h: %d\n", h); + return NS_FAIL; } int redraw(void *xd) { - puts("redraw"); - return 0; + fprintf(stderr, NS_PREFIX "redraw\n"); + return NS_FAIL; } int redraw_xywh(void *xd, int x, int y, int w, int h) { - printf("redraw_xywh: %d,%d %dx%d\n", x, y, w, h); - return 0; + fprintf(stderr, NS_PREFIX "redraw_xywh: %d,%d %dx%d\n", x, y, w, h); + return NS_FAIL; } -/* redraw a button bar */ -void -redraw_buttons(buttonbar_t *bbar) -{ - bbar_calc_button_sizes(bbar); - bbar_calc_button_positions(bbar); - bbar_draw(bbar, IMAGE_STATE_CURRENT, MODE_MASK); -} +button_t *screen_button_create(char *text,char code) { + button_t *b; + char p[3]; + + if(!text||!*text||!(b=button_create(text))) + return NULL; + + p[0]=NS_SCREEN_ESCAPE; + p[1]=code; + p[2]='\0'; + + button_set_action(b, ACTION_ECHO, p); + + return b; } @@ -2347,58 +2358,53 @@ redraw_buttons(buttonbar_t *bbar) int ins_disp(void *xd, int after, char *name) { - buttonbar_t *bbar = *((buttonbar_t **) xd); + buttonbar_t *bbar; button_t *button; - int state = 1; - char p[3] = "\x01x"; + + if(!xd||!name||!*name) + return NS_FAIL; + bbar = *((buttonbar_t **) xd); + + if(!(button=screen_button_create(name,'0' + after + 1))) + return NS_FAIL; #ifdef NS_DEBUG - fprintf(stderr, "ins_disp: %s after %d...\n", name, after); + fprintf(stderr, NS_PREFIX "ins_disp: %s after %d...\n", name, after); #endif - if (!bbar) { - if (!(bbar = bbar_create())) { - fprintf(stderr, "ins_disp: failed to create button-bar...\n"); - return 0; - } else { - bbar_set_font(bbar, "-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1"); - bbar_set_docked(bbar, BBAR_DOCKED_TOP); - } - state = 0; - } + if((bbar=bbar_insert_button(bbar,button,after,FALSE))) { + *((buttonbar_t **) xd) = bbar; + return NS_SUCC; } - button = button_create(name); - p[1] = '0' + after + 1; - button_set_action(button, ACTION_ECHO, p); - if (!bbar->buttons || after < 0) { /* first button */ - button->next = bbar->buttons; - bbar->buttons = button; - } else { - button_t *b = bbar->buttons; - while (after-- > 0 && b->next) - b = b->next; - button->next = b->next; - b->next = button; - } + button_free(button); + return NS_FAIL; +} - bbar->current = button; - *((buttonbar_t **) xd) = bbar; /* ugly, but it will remain here till - bbar_event_init_dispatcher() - takes a bbar parameter */ - if (!state) { - bbar_init(bbar, TermWin.width); - bbar_add(bbar); - } - redraw_buttons(bbar); +/* add supa-dupa right buttons for screen-features. + if our user's configured a bbar, we'll add to that, + otherwise, we'll create one. */ +int +add_screen_ctl_button(buttonbar_t **xd,char *name,char key) +{ + buttonbar_t *bbar; + button_t *button; - if (!state) { - parent_resize(); - } + if(!xd||!name||!*name) + return NS_FAIL; + bbar = *xd; - return 0; + if(!(button=screen_button_create(name,key))) + return NS_FAIL; + + if((bbar=bbar_insert_button(bbar,button,-1,TRUE))) { + *xd = bbar; + return NS_SUCC; } + + button_free(button); + return NS_FAIL; } @@ -2415,11 +2421,11 @@ del_disp(void *xd, int n) int c; for (c = 0, b2 = bbar->buttons; b2; c++, b2 = b2->next) - fprintf(stderr, "%02d: \"%s\"\n", c, b2->text); + fprintf(stderr, NS_PREFIX "del_disp: %02d: \"%s\"\n", c, b2->text); #endif if (!bbar || !(button = bbar->buttons)) - return 0; + return NS_FAIL; b2 = button = bbar->buttons; if (!n) { @@ -2430,8 +2436,8 @@ del_disp(void *xd, int n) while (n-- > 0) { b2 = button; if (!(button = button->next)) { - fprintf(stderr, "cannot delete button %d: does not exist...\n", bi); - return -1; + fprintf(stderr, NS_PREFIX "del_disp: cannot delete button %d: does not exist...\n", bi); + return NS_FAIL; } } b2->next = button->next; @@ -2440,17 +2446,15 @@ del_disp(void *xd, int n) } #ifdef NS_DEBUG_ - fprintf(stderr, "deleting button %d (%s)...\n", bi, button->text); + fprintf(stderr, NS_PREFIX "del_disp: deleting button %d (%s)...\n", bi, button->text); #endif button->next = NULL; button_free(button); - redraw_buttons(bbar); + bbar_redraw(bbar); - *((buttonbar_t **) xd) = bbar; /* ugly */ - - return 0; + return NS_SUCC; } @@ -2468,7 +2472,7 @@ upd_disp(void *xd, int n, int flags, char *name) button_t *button; if (!bbar || !(button = bbar->buttons)) - return 0; + return NS_FAIL; button = bbar->buttons; while (n-- > 0 && button->next) @@ -2480,7 +2484,7 @@ upd_disp(void *xd, int n, int flags, char *name) if (!(button->text = strdup(name))) { button->len = 0; - return -1; + return NS_OOM; } button->len = strlen(name); @@ -2489,11 +2493,9 @@ upd_disp(void *xd, int n, int flags, char *name) if (flags >= 0) button->flags = flags; - redraw_buttons(bbar); + bbar_redraw(bbar); - *((buttonbar_t **) xd) = bbar; /* ugly */ - - return 0; + return NS_SUCC; } @@ -2502,11 +2504,9 @@ upd_disp(void *xd, int n, int flags, char *name) int err_msg(void *xd, int err, char *msg) { -#ifdef NS_DEBUG - if (err != NS_SCREEN_ST_CLR) - printf("err_msg #%d: \"%s\"\n", err, msg); -#endif - return 0; + if(strlen(msg)) + menu_dial(NULL,msg,0,NULL,NULL); + return NS_SUCC; } @@ -2516,11 +2516,26 @@ int inp_text(void *xd, int id, char *txt) { tt_write(txt, strlen(txt)); - return 0; + return NS_SUCC; } +/* open a dialog */ +int +inp_dial(void *xd, char *prompt, int maxlen, char **retstr, + int (*inp_tab)(void *,char *,size_t,size_t)) +{ + switch(menu_dial(xd, prompt, maxlen, retstr,inp_tab)) { + case 0: + return NS_SUCC; + case -2: + return NS_USER_CXL; + default: + return NS_FAIL; }} + + + /* run a program (normally "screen") inside the terminal */ int exe_prg(void *xd, char **argv) @@ -2558,6 +2573,8 @@ init_command(char **argv) ns_register_exe(efuns, exe_prg); ns_register_txt(efuns, inp_text); + ns_register_inp(efuns, inp_dial); + ns_register_tab(efuns, menu_tab); #endif /* Initialize the command connection. This should be called after @@ -2588,8 +2605,60 @@ init_command(char **argv) if (!TermWin.screen_mode) cmd_fd = run_command(argv); - else if ((TermWin.screen = ns_attach_by_URL(rs_url, &efuns, &ns_err, (void *) &buttonbar))) + else if ((TermWin.screen = ns_attach_by_URL(rs_url, rs_hop, &efuns, &ns_err, (void *) &buttonbar))) { + button_t *button; + menu_t *m; + menuitem_t *i; + if((m=menu_create(NS_MENU_TITLE))) { + char *sc[]={ "New", "\x01\x03", "Close", "\x01k" }; + int n,nsc=sizeof(sc)/sizeof(char *); + + if(menu_list) { + for(n=0;nnummenus;n++) { /* blend in w/ l&f */ +#ifdef NS_DEBUG + fprintf(stderr,NS_PREFIX "font: %d: %p\n",n,menu_list->menus[n]->font); +#endif + if(menu_list->menus[n]->font) { + m->font =menu_list->menus[n]->font; + m->fwidth =menu_list->menus[n]->fwidth; + m->fheight=menu_list->menus[n]->fheight; +#ifdef MULTI_CHARSET + m->fontset=menu_list->menus[n]->fontset; +#endif + break; }}} + + for(n=0;n<(nsc-1);n+=2) { + if((i=menuitem_create(sc[n]))) { +# ifdef NS_DEBUG + fprintf(stderr, NS_PREFIX "register %s (%d)\n",&sc[n+1][1],*sc[n+1]); +# endif + menuitem_set_action(i,MENUITEM_ECHO,sc[n+1]); + menu_add_item(m,i); }} + + if((i=menuitem_create("About..."))) { + menuitem_set_action(i,MENUITEM_ALERT,"Screen compatibility layer by Azundris "); + menu_add_item(m,i); } + + if((button=button_create(NS_MENU_TITLE))) { + if(!(buttonbar=bbar_insert_button(buttonbar,button,-1,TRUE))) { + m->font=NULL; +#ifdef MULTI_CHARSET + m->fontset=NULL; +#endif + menu_delete(m); + button_set_action(button,ACTION_STRING,NS_MENU_TITLE); + button_free(button); } + else { + int j,k=menu_list?menu_list->nummenus:0; + menu_list=menulist_add_menu(menu_list,m); + for (j=k;jnummenus;j++) + event_data_add_mywin(&menu_event_data,menu_list->menus[j]->win); + if(!k) + menu_init(); + button_set_action(button,ACTION_MENU,NS_MENU_TITLE); }}} +/* add_screen_ctl_button(&buttonbar,"New",'c'); */ cmd_fd = TermWin.screen->fd; + } # undef ETERM_PREFIX # undef ESCREEN_PREFIX if (cmd_fd < 0) { diff --git a/src/libscream.c b/src/libscream.c index af84996..e2b153d 100644 --- a/src/libscream.c +++ b/src/libscream.c @@ -1,9 +1,17 @@ /**************************************************************************** - * scream::libscream.c + * scream::libscream.c Azundris + * * routines to connect to screen and or scream daemons. - * GNU Public Licence applies. + * libscream is a double-transparency layer -- it abstracts the backend + * (screen or a replacement, locally or ssh-tunneled) to the front-end + * (a terminal-emulation such as Eterm) and vice versa. + * + * Lesser GNU Public Licence applies. + * Thread-safe: untested * 2002/04/19 Azundris incept * 2002/05/04 Azundris support for esoteric screens, thanks to Till + * 2002/05/12 Azundris edit display names, send statement, tab completion + * 2002/05/13 Azundris ssh tunnel through firewall ***************************************************************************/ @@ -12,11 +20,14 @@ #include /* bzero() */ #include /* getpwuid() */ #include /* getpwuid() */ +#include /* stat() */ #include /* getuid() */ #include /* atoi() */ #include /* getservbyname() */ #include /* ntohs() */ #include /* PATH_MAX */ +#include /* isspace() */ +#include /* errno */ #include "scream.h" /* structs, defs, headers */ #include "screamcfg.h" /* user-tunables */ @@ -31,7 +42,9 @@ -long err_inhibit = 0; /* bits. avoid telling user the same thing twice. */ +static long err_inhibit = 0; /* bits. avoid telling same error twice. */ +static _ns_sess *sa = NULL; /* anchor for session list */ +static _ns_hop *ha = NULL; /* anchor for hop list */ @@ -41,6 +54,128 @@ long err_inhibit = 0; /* bits. avoid telling user the same thing twice +/* ns_new_hop. create and initialize a hop struct. + lp local port. if 0: if otherwise matching hop exists, reuse that. + otherwise, find the first free (as in, not used + by us) port, starting with NS_MIN_PORT. + fw firewall machine. numeric or symbolic. + fp foreign port. if 0: default to SSH port. + delay wait n seconds for tunnel to come up before trying to use it + s the session to add the hop to + <- a matching (existing or newly created) hop structure, or NULL */ + +_ns_hop * +ns_new_hop(int lp,char *fw,int fp,int delay,_ns_sess *s) { + _ns_hop *h=ha; + + if(!fw||!*fw) + return NULL; + + if(!fp) + fp=get_ssh_port(); /* remote port defaults to SSH */ + + if(s) { + /* see if we already have a matching hop. */ + while(h&&!(((h->localport==lp)||(!lp))&& + (!strcmp(h->fw,fw))&& + (h->fwport==fp)&& + (h->sess->port==s->port)&&(!strcmp(h->sess->host,s->host)))) + h=h->next; + + if(h) { + if(delay) + h->delay=delay; /* may change delay! */ + h->refcount++; + return h; }} + + h = malloc(sizeof(_ns_hop)); + if (h) { + bzero(h, sizeof(_ns_hop)); + if((h->fw=strdup(fw))) { + if(!lp) { + lp=NS_MIN_PORT; /* local port defaults to */ + if(ha) { /* NS_MIN_PORT. if that's */ + int f; /* taken, use next free port. */ + do { /* free as in, not used by us. */ + _ns_hop *i=ha; + f=0; + while(i) + if(i->localport==lp) { + f=1; + lp++; + i=NULL; } + else + i=i->next; + } while(f); + } + } + h->delay =(delay?delay:NS_TUNNEL_DELAY); + h->localport=lp; + h->fwport =fp; + h->refcount++; + h->next =ha; + h->sess =s; + ha=h; } + else { + free(h); + return NULL; }} + + return h; } + + + +/* ns_dst_hop. deref (and, where necessary, release) a hop struct. + if sp is provided, additional integrity magic will take place. + ss hop to deref/free + sp session that the hop used to belong to (NULL for none (as if)) + <- NULL */ + +_ns_hop * +ns_dst_hop(_ns_hop ** ss,_ns_sess *sp) +{ + if (ss && *ss) { + _ns_hop *s = *ss; + +#ifdef NS_DEBUG_MEM + if (s->refcount<=0) { + fprintf(stderr,NS_PREFIX "ns_dst_hop: leak alert -- trying to double-free hop...\n"); + return NULL; } +#endif + + if (!--(s->refcount)) { /* was last ref to hop => free hop */ + if(s->fw) + free(s->fw); +#ifdef NS_DEBUG_MEM + bzero(s, sizeof(_ns_hop)); +#endif + if(ha==s) /* delist */ + ha=s->next; + else { + _ns_hop *h=ha; + while(h&&h->next!=s) + h=h->next; + if(h) + h->next=s->next; } + free(s); + } + else if(sp&&sp->hop==s) { + /* hop shouldn't point back at a session that just dereffed it + as it's probably about to die. fix the back ref to a session + that's actually valid. */ + _ns_sess *p=sa; + while(p&&((p==sp)||(p->port!=sp->port)||(strcmp(p->host,sp->host)))) + p=p->next; + if(!p) + ns_desc_hop(s,NS_PREFIX "ns_dst_sess: Leak alert -- found a hop that is only\n referenced once, but has a refcount > 1. Hop data follow"); + else + s->sess=p; + } + *ss = NULL; + } + return NULL; } + + + _ns_efuns * ns_new_efuns(void) { @@ -66,9 +201,7 @@ ns_dst_efuns(_ns_efuns ** ss) { if (ss && *ss) { _ns_efuns *s = *ss; -#ifdef NS_DEBUG_MEM *ss = NULL; -#endif if (!--(s->refcount)) { #ifdef NS_DEBUG_MEM bzero(s, sizeof(_ns_efuns)); @@ -91,6 +224,8 @@ ns_new_disp(void) return s; } +_ns_sess *ns_dst_sess(_ns_sess **); /* forward, sorry */ + _ns_disp * ns_dst_disp(_ns_disp ** ss) { @@ -100,8 +235,10 @@ ns_dst_disp(_ns_disp ** ss) free(s->name); if (s->efuns) ns_dst_efuns(&(s->efuns)); -#ifdef NS_DEBUG_MEM + if (s->child) /* nested screen? */ + ns_dst_sess(&(s->child)); /* forward, sorry */ *ss = NULL; +#ifdef NS_DEBUG_MEM bzero(s, sizeof(_ns_disp)); #endif free(s); @@ -114,9 +251,7 @@ ns_dst_dsps(_ns_disp ** ss) { if (ss && *ss) { _ns_disp *s = *ss, *t; -#ifdef NS_DEBUG_MEM *ss = NULL; -#endif do { t = s->next; ns_dst_disp(&s); @@ -136,6 +271,15 @@ ns_new_sess(void) bzero(s, sizeof(_ns_sess)); s->escape = NS_SCREEN_ESCAPE; /* default setup for the screen program */ s->literal = NS_SCREEN_LITERAL; + s->dsbb = NS_SCREEN_DEFSBB; + if (sa) { /* add to end of list */ + _ns_sess *r=sa; + while(r->next) + r=r->next; + r->next=s; + } + else + sa=s; } return s; } @@ -146,6 +290,8 @@ ns_dst_sess(_ns_sess ** ss) if (ss && *ss) { _ns_sess *s = *ss; ns_dst_dsps(&(s->dsps)); + if (s->hop) + ns_dst_hop(&(s->hop),s); if (s->host) free(s->host); if (s->user) @@ -154,8 +300,14 @@ ns_dst_sess(_ns_sess ** ss) free(s->pass); if (s->efuns) ns_dst_efuns(&(s->efuns)); -#ifdef NS_DEBUG_MEM + if (s->prvs) + s->prvs->next=s->next; + else + sa=s->next; /* align anchor */ + if (s->next) + s->next->prvs=s->prvs; *ss = NULL; +#ifdef NS_DEBUG_MEM bzero(s, sizeof(_ns_sess)); #endif free(s); @@ -184,13 +336,19 @@ ns_screen_command(_ns_sess * sess, char *cmd) { char *c; int ret = NS_SUCC; + + if (!cmd||!*cmd) + return NS_FAIL; + if (sess->efuns->inp_text) { if ((c = strdup(cmd))) { - char *p = c; - while (*p) { - if (*p == NS_SCREEN_ESCAPE) /* replace default escape-char with that */ - *p = sess->escape; /* actually used in this session */ - p++; + { + char *p = c; /* replace default escape-char with that */ + while (*p) { /* actually used in this session */ + if (*p == NS_SCREEN_ESCAPE) + *p = sess->escape; + p++; + } } sess->efuns->inp_text(NULL, sess->fd, c); free(c); @@ -199,13 +357,34 @@ ns_screen_command(_ns_sess * sess, char *cmd) } /* out of memory */ else { ret = NS_EFUN_NOT_SET; - fprintf(stderr, "ns_screen_command: sess->efuns->inp_text not set!\n"); + fprintf(stderr, NS_PREFIX "ns_screen_command: sess->efuns->inp_text not set!\n"); } return ret; } +/* send a single command string to screen, adding the equiv of ^A: + s the session + cmd the command string + <- error code */ + +int ns_screen_xcommand(_ns_sess *s,char prefix,char *cmd) { + char *i; + int ret=NS_OOM; + if((i=malloc(strlen(cmd)+4))) { + size_t l=strlen(cmd)+2; + strcpy(&i[2],cmd); + i[0]=s->escape; + i[1]=prefix; + i[l]='\n'; + i[++l]='\0'; + ret=ns_screen_command(s,i); + free(i); } + return ret; } + + + /* scroll horizontally to column x (dummy) */ int ns_scroll2x(_ns_sess * s, int x) @@ -271,51 +450,164 @@ ns_upd_stat(_ns_sess * s) +/* ns_inp_dial + open a dialog + s the session + !retstr where we'll store a pointer to the result (the user's input) + prompt the prompt to appear in the dialog box + <- msg */ + + +int ns_inp_dial(_ns_sess *s,char *prompt, int maxlen, char **retstr, + int (*inp_tab)(void *,char *,size_t,size_t)) { + char *c; + int ret = NS_SUCC; + if (s->efuns->inp_dial) { + (void)s->efuns->inp_dial((void *)s, prompt, maxlen, retstr, inp_tab); + } + else { + ret = NS_EFUN_NOT_SET; + fprintf(stderr, NS_PREFIX "ns_screen_command: sess->efuns->inp_dial not set!\n"); + } + return ret; } + + + /***************************************************************************/ /* attach/detach */ /*****************/ +/* ns_sess_init + init an opened session (transmit .screenrc, or whatever) + sess the session + <- error code */ + +int +ns_sess_init(_ns_sess * sess) { + if((sess->backend==NS_MODE_NEGOTIATE)||(sess->backend==NS_MODE_SCREEN)) + return ns_parse_screenrc(sess); + return NS_SUCC; } + /* return port number for service SSH (secure shell). - <- a port number -- 22 in all likelihood. - */ + <- a port number -- 22 in all likelihood. */ + int get_ssh_port(void) { + static int port=0; + struct servent *srv; + if(port) + return port; /* (fixme) replace with getservbyname_r on systems that have it */ - struct servent *srv = getservbyname("ssh", "tcp"); - return srv ? ntohs(srv->s_port) : NS_DFLT_SSH_PORT; + srv = getservbyname("ssh", "tcp"); + return (port=(srv ? ntohs(srv->s_port) : NS_DFLT_SSH_PORT)); } + +/* ns_parse_hop + parse a hop-string into a hop-struct + h: one of NULL lclport:fw:fwport fw:fwport lclport:fw + if set, describes how to tunnel through a fw to access an URL + describing a target behind said firewall + <- a hop struct, or NULL +*/ + +_ns_hop *ns_parse_hop(_ns_sess *s,char *h) { + char *p=h,*e,*fw=NULL; + int f=0,v,lp=0,fp=0,delay=0; + + if(!h||!*h) + return NULL; + + if((e=strrchr(h,','))) { + *(e++)='\0'; + if(*e) + delay=atoi(e); } + + while(*p&&*p!=':') + if(!isdigit(*(p++))) + f=1; + + if(!*p) /* fw only */ + return ns_new_hop(lp,h,fp,delay,s); + + if(!f) { /* lp:fw... */ + if(!(v=atoi(h))) + return NULL; + lp=v; + e=++p; + while(*e&&*e!=':') + e++; + if(*e) { + *(e++)='\0'; + if(!(v=atoi(e))) + return NULL; + fp=v; }} + else { /* fw:fp */ + *(p++)='\0'; + if(!(v=atoi(p))) + return NULL; + fp=v; + p=h; } + return ns_new_hop(lp,p,fp,delay,s); } + + + +/* ns_desc_hop + print basic info about a hop (tunnel, firewall). mostly for debugging. + hop: a hop struct as generated by (eg) ns_attach_by_URL() + doc: info about the context + ! stdout: info about the hop */ + +void +ns_desc_hop(_ns_hop *h, char *doc) { + if(!h&&doc) { + fprintf(stderr, NS_PREFIX "%s: ns_desc_hop called with broken pointer!\n", doc); + return; } + + if(doc) + fprintf(stderr,NS_PREFIX "%s:\n",doc); + + fprintf(stderr,NS_PREFIX "tunnel from localhost:%d to %s:%d to %s:%d is %s. (delay %d, %d ref%s)\n", + h->localport,h->fw,h->fwport, + h->sess->host,h->sess->port, + h->established?"up":"down",h->delay, + h->refcount,h->refcount==1?"":"s"); } + + + /* ns_desc_sess print basic info about a session. mostly for debugging. sess: a session struct as generated by (eg) ns_attach_by_URL() doc: info about the context - ! stdout: info about the session -*/ + ! stdout: info about the session */ void ns_desc_sess(_ns_sess * sess, char *doc) { if (!sess) { - fprintf(stderr, "%s: ns_desc_sess called with broken pointer!\n", doc); + fprintf(stderr, NS_PREFIX "%s: ns_desc_sess called with broken pointer!\n", doc); return; } if (sess->where == NS_LCL) - fprintf(stderr, "%s: (efuns@%p)\t (user %s) local %s ", doc, sess->efuns, sess->user, sess->proto); + fprintf(stderr, NS_PREFIX "%s: (efuns@%p)\t (user %s) local %s", doc, sess->efuns, sess->user, sess->proto); else { - fprintf(stderr, "%s: (efuns@%p)\t %s://%s%s%s@%s", + fprintf(stderr, NS_PREFIX "%s: (efuns@%p)\t %s://%s%s%s@%s", doc, sess->efuns, sess->proto, sess->user, sess->pass ? ":" : "", sess->pass ? sess->pass : "", sess->host); if (sess->port != NS_DFLT_SSH_PORT) fprintf(stderr, ":%s", sess->port); } - fprintf(stderr, "/%s\n", sess->rsrc); -} + fprintf(stderr, "%c%s\n", sess->where==NS_LCL?' ':'/',sess->rsrc); + if(sess->hop) + ns_desc_hop(sess->hop,NULL); + if(sess->home) + fprintf(stderr, NS_PREFIX "info: searching for rc in %s\n", sess->home); } @@ -325,8 +617,8 @@ ns_desc_sess(_ns_sess * sess, char *doc) ns_run() will fail if no callback to the terminal's "run program" (exec) facility is provided. cmd: a string to exec - <- whatever the callback returns. In Eterm, it's a file-descriptor. - */ + <- whatever the callback returns. In Eterm, it's a file-descriptor. */ + int ns_run(_ns_efuns * efuns, char *cmd) { @@ -339,7 +631,7 @@ ns_run(_ns_efuns * efuns, char *cmd) if (cmd && *cmd) { /* count args (if any) */ #ifdef NS_DEBUG - fprintf(stderr, "ns_run: executing \"%s\"...\n", cmd); + fprintf(stderr, NS_PREFIX "ns_run: executing \"%s\"...\n", cmd); #endif do { n++; @@ -417,7 +709,8 @@ ns_attach_lcl(_ns_sess ** sp) if (!sp || !*sp) return NS_FAIL; sess = *sp; - ret = snprintf(cmd, MAXCMD, "%s %s", NS_SCREEN_CALL, NS_SCREEN_OPTS); + ret = snprintf(cmd, MAXCMD, "%s %s", NS_SCREEN_CALL, + sess->rsrc?sess->rsrc:NS_SCREEN_OPTS); return (ret < 0 || ret > MAXCMD) ? NS_FAIL : ns_run(sess->efuns, cmd); } @@ -436,8 +729,33 @@ ns_attach_ssh(_ns_sess ** sp) if (!sp || !*sp) return NS_FAIL; + sess = *sp; - ret = snprintf(cmd, MAXCMD, "%s %s -p %d %s@%s %s", NS_SSH_CALL, NS_SSH_OPTS, sess->port, sess->user, sess->host, NS_SCREEM_CALL); + + if(sess->hop) { + if(sess->hop->established==NS_HOP_DOWN) { /* the nightmare foe */ + ret = snprintf(cmd, MAXCMD, "%s %s -p %d -L %d:%s:%d %s@%s", + NS_SSH_CALL,NS_SSH_TUNNEL_OPTS, + sess->hop->fwport, + sess->hop->localport, + sess->host,sess->port, + sess->user, sess->hop->fw, + NS_SCREEM_CALL); + if(ret < 0 || ret > MAXCMD) + return NS_FAIL; + ns_run(sess->efuns, cmd); + sleep(sess->hop->delay); + } + ret = snprintf(cmd, MAXCMD, "%s %s -p %d %s@localhost %s", + NS_SSH_CALL,NS_SSH_OPTS, + sess->hop->localport, sess->user, + NS_SCREEM_CALL); } + else { + ret = snprintf(cmd, MAXCMD, "%s %s -p %d %s@%s %s", + NS_SSH_CALL,NS_SSH_OPTS, + sess->port, sess->user, sess->host, + NS_SCREEM_CALL); } + return (ret < 0 || ret > MAXCMD) ? NS_FAIL : ns_run(sess->efuns, cmd); } @@ -486,11 +804,10 @@ ns_attach_by_sess(_ns_sess ** sp, int *err) } #ifdef NS_DEBUG - fprintf(stderr, "screen session-fd is %d\n", sess->fd); + fprintf(stderr, NS_PREFIX "ns_attach_by_sess: screen session-fd is %d\n", sess->fd); #endif - (void) ns_screen_command(sess, NS_SCREEN_INIT); - + (void)ns_sess_init(sess); return sess; fail: @@ -506,6 +823,9 @@ ns_attach_by_sess(_ns_sess ** sp, int *err) proto://user:password@host.domain:port (all parts optional) NULL/empty string equivalent to screen://current_user@localhost/-xRR + hop: one of NULL lclport:fw:fwport fw:fwport lclport:fw + if set, describes how to tunnel through a fw to access an URL + describing a target behind said firewall ef: a struct containing callbacks into client (resize scrollbars etc.) while setting those callbacks is optional; omitting the struct itself seems unwise. @@ -517,10 +837,10 @@ ns_attach_by_sess(_ns_sess ** sp, int *err) */ _ns_sess * -ns_attach_by_URL(char *url, _ns_efuns ** ef, int *err, void *xd) +ns_attach_by_URL(char *url, char *hop,_ns_efuns ** ef, int *err, void *xd) { int err_dummy; - char *p; + char *p,*d=NULL; _ns_sess *sess = ns_new_sess(); struct passwd *pwe = getpwuid(getuid()); @@ -532,7 +852,7 @@ ns_attach_by_URL(char *url, _ns_efuns ** ef, int *err, void *xd) return NULL; if (url && strlen(url)) { - char *q, *d; + char *q; if (!(d = strdup(url))) goto fail; @@ -559,6 +879,72 @@ ns_attach_by_URL(char *url, _ns_efuns ** ef, int *err, void *xd) q = p + 1; } + if ((p = strchr(q, '/'))) { + *(p++) = '\0'; + if(strlen(p)) { + char *r=p; + int f; + while(*r) { + if(*r=='+') + *(r++)=' '; + else if((*r=='%')&&(strlen(r)>2)) { + long v; + char *e; + char b[3]; + b[0]=r[1]; + b[1]=r[2]; + b[2]='\0'; + v=strtol(b,&e,16); + if(!*e) { + *(r++)=(char)(v&0xff); + memmove(r,&r[2],strlen(&r[2])); + } + } + else + r++; } + r=p; + f=0; + while(*r) { + if(*r==' ') { /* Padding between arguments */ + while(*r==' ') + r++; } + else { + if(*r=='-') { + if(*(++r)=='e') { /* set escape */ + char x=0,y=0; + while(*(++r)==' ') + ; + if((x=ns_parse_esc(&r))&&(y=ns_parse_esc(&r))) { + sess->escape =x; + sess->literal=y; }} + else if(*r=='c') { /* alt screenrc */ + char *rc,*rx; + while(*(++r)==' ') + ; + if((rx=strchr(r,' '))) + *rx='\0'; + if(*r!='/') + fprintf(stderr, NS_PREFIX "URL: path for screen's option -c should be absolute (%s)\n",r); + if((rc=strdup(r))) { + if(sess->home) /* this should never happen */ + free(sess->home); +#ifdef NS_DEBUG + fprintf(stderr, NS_PREFIX "URL: searching for rc in %s\n",rc); +#endif + sess->home=rc; } + if(rx) { + r=rx; + *rx=' '; }} + while(*r&&(f||*r!=' ')) { + if(*r=='\"') + f=1-f; + r++; }} + while(*r&&*r!=' ') /* proceed to space */ + r++; }} + + if(!(sess->rsrc=strdup(p))) + goto fail; }} + if ((p = strchr(q, ':'))) { /* port, if any */ *(p++) = '\0'; if (!*p || !(sess->port = atoi(p)) || sess->port > NS_MAX_PORT) { @@ -584,6 +970,16 @@ ns_attach_by_URL(char *url, _ns_efuns ** ef, int *err, void *xd) goto fail; } else if (pwe && strcmp(pwe->pw_name, sess->user)) { /* user!=current_user */ sess->where = NS_SU; + if(!(pwe=getpwnam(sess->user))) { + *err = NS_UNKNOWN_USER; + goto fail; } + } + + if (pwe&&!sess->home) { + if((sess->home=malloc(strlen(pwe->pw_dir)+strlen(NS_SCREEN_RC)+2))) + sprintf(sess->home,"%s/%s",pwe->pw_dir,NS_SCREEN_RC); + else + goto fail; } if (!sess->host) { /* no host */ @@ -617,10 +1013,19 @@ ns_attach_by_URL(char *url, _ns_efuns ** ef, int *err, void *xd) sess->userdef = xd; + if (hop && strlen(hop)) { + sess->hop=ns_parse_hop(sess,hop); + if(sess->hop&&(!strcmp(sess->host,sess->hop->fw)||!strcmp(sess->host,"localhost")||!strcmp(sess->host,"127.0.0.1"))) + fprintf(stderr,NS_PREFIX "ns_attach_by_URL: routing in circles...\n"); + } + *err = NS_SUCC; return ns_attach_by_sess(&sess, err); fail: + if(d) + free(d); + return ns_dst_sess(&sess); } @@ -734,6 +1139,24 @@ ns_register_txt(_ns_efuns * efuns, int (*inp_text) (void *, int, char *)) +/* function that will open a dialog */ +void +ns_register_inp(_ns_efuns * efuns, int (*inp_dial)(void *,char *,int,char **,int (*)(void *,char *,size_t,size_t))) +{ + efuns->inp_dial = inp_dial; +} + + + +/* function that will handle tab-completion in a dialog */ +void +ns_register_tab(_ns_efuns * efuns, int (*inp_tab)(void *,char *[],int,char *,size_t,size_t)) +{ + efuns->inp_tab = inp_tab; +} + + + /* get callbacks. at least one of session and display must be non-NULL. s session, or NULL. if NULL, will be initialized from d->sess d display, or NULL. if NULL, will be initialized from s->curr. @@ -916,6 +1339,295 @@ disp_kill(_ns_disp * d3) +/* tab completion for screen-commands + !b current entry (changes) + l number of characters to compare in current entry + m maximum number of characters in entry (size of input buffer) + <- error code */ + +int +ns_inp_tab(void *xd,char *b,size_t l,size_t m) { + char *sc[]={ "acladd","addacl","aclchg","chacl","acldel","aclgrp", + "aclumask","umask","activity", + "allpartial","at","attrcolor","autonuke","bce", + "bell_msg","bind","bindkey","break","breaktype", + "bufferfile","c1","caption","charset","chdir", + "clear","compacthist","console","copy", + "crlf","debug","defc1","defautonuke","defbce", + "defbreaktype","defcharset","defflow","defgr", + "defencoding","deflog","deflogin","defmode", + "defmonitor","defobuflimit","defscrollback", + "defshell","defsilence","defslowpast","defutf8", + "defwrap","defwritelock","defzombie","detach", + "dinfo","displays","digraph","dumptermcap", + "escape","eval","exec","fit","flow","focus","gr", + "hardcopy","hardcopy_append","hardcopydir", + "height","help","history","ignorecase","encoding", + "kill","license","lockscreen","log","logfile", + "login","logtstamp","mapdefault","mapnotnext", + "maptimeout","markkeys","meta","monitor", + "multiuser","nethack","next","nonblock","number", + "obuflimit","only","other","partial","password", + "paste","pastefont","pow_break","pow_detach", + "prev","printcmd","process","quit","readbuf", + "readreg","redisplay","remove","removebuf","reset", + "resize","screen","scrollback","select", + "sessionname","setenv","setsid","shell", + "shelltitle","silence","silencewait","sleep", + "slowpast","source","sorendition","split","stuff", + "su","suspend","term","termcap","terminfo", + "termcapinfo","unsetenv","utf8","vbell", + "vbell_msg","vbellwait","verbose","version", + "width","windowlist","windows","wrap","writebuf", + "writelock","xoff","xon","zombie" }; + + _ns_sess *s =(_ns_sess *)xd; + int nsc=sizeof(sc)/sizeof(char *); + + if (s->efuns->inp_tab) + return s->efuns->inp_tab((void *)s,sc,nsc,b,l,m)<0?NS_FAIL:NS_SUCC; + + fprintf(stderr, NS_PREFIX "ns_screen_command: sess->efuns->inp_tab not set!\n"); + return NS_EFUN_NOT_SET; } + + + +/* parse argument to screen's "escape" statement. + x points to the char to process + screen-manual says this can be one of x ^X \123 or \\ \^ ... + !x the pointer is advanced to the next segment (from esc to literal etc.) + <- return as char ('\0' -> fail) */ + +char ns_parse_esc(char **x) { + char r='\0'; + + if(**x=='\\') { + (*x)++; + r=**x; + if(r>='0'&&r<='7') { /* octal, otherwise literal */ + char b[4]="\0\0\0"; + char *e=*x; + long v; + size_t l=0; + while((*e>='0'&&*e<='7')&&(l<3)) { /* can't use endptr here : ( */ + e++; l++; } + *x=&e[-1]; + while(--l) + b[l]=*(--e); + r=(char)strtol(b,&e,8); + } + } + else if(**x=='^') { + (*x)++; + r=**x; + if(r>='A'&&r<='Z') + r=1+r-'A'; + else if(r>='a'&&r<='z') + r=1+r-'a'; + else + r='\0'; } /* malformed */ + else + r=**x; + + if(**x) + (*x)++; + return r; } + + + +/* ns_parse_screen_cmd + parse a command the user intends to send to the screen program, + either via .screenrc or using ^A: + s the affected (current) session. s->current should be set. + p the command + <- error code */ + +int ns_parse_screen_cmd(_ns_sess *s,char *p) { + char *p2; + long v1=-1; + + if(!p||!*p) + return NS_FAIL; + + if((p2=strchr(p,' '))) { /* first argument */ + char *e; + while(isspace(*p2)) + p2++; + v1=strtol(p2,&e,0); /* magic conversion mode */ + if((p2==e)||(v1<0)) + v1=-1; } + +#define IS_CMD(b) (strncasecmp(p,b,strlen(b))==0) + if(!p2) + fprintf(stderr, NS_PREFIX "screenrc: ignoring \"%s\" without an argument...\n",p); + else if(IS_CMD("defescape")) + fprintf(stderr, NS_PREFIX "screenrc: ignoring \"defescape\", did you mean \"escape\"?\n"); + else if(IS_CMD("defhstatus")||IS_CMD("hardstatus")||IS_CMD("echo")|| + IS_CMD("colon")||IS_CMD("nethack")|| + IS_CMD("info")||IS_CMD("time")||IS_CMD("title")||IS_CMD("wall")|| + IS_CMD("lastmsg")||IS_CMD("msgwait")||IS_CMD("msgminwait")) + fprintf(stderr, NS_PREFIX "screenrc: ignoring \"%s\", not applicable...\n",p); + else if(IS_CMD("escape")) { + char x=0,y=0; + if((x=ns_parse_esc(&p2))&&(y=ns_parse_esc(&p2))) { + s->escape =x; + s->literal=y; + return NS_SUCC; } + else + fprintf(stderr, NS_PREFIX "screenrc: ignoring \"escape\" because of invalid arguments %o %o...\n",x,y); } + else if(IS_CMD("defscrollback")) { + if(v1dsbb=v1; + return NS_SUCC; }} + else if(IS_CMD("scrollback")) { + if(v1curr) + s->curr=s->dsps; + if(!s->curr) + fprintf(stderr,NS_PREFIX "screenrc: ignoring \"%s\", cannot determine current display!?...\n",p); + else + s->curr->sbb=v1; + return NS_SUCC; }} + else { +#ifdef NS_DEBUG + fprintf(stderr, NS_PREFIX "screenrc: bored now \"%s\"\n",p); +#endif + return NS_SUCC; } + return NS_FAIL; } + + + +/* ns_parse_screen_key + parse and forward a screen-hotkey + s the session to forward to + c the character following the escape-char. (when we got here, + we already got (and threw out) a screen-escape, so we'll have + to also send one if we ever forward c to the screen program. + <- error code */ + +int ns_parse_screen_key(_ns_sess *s,char c) { + char *i=NULL; + char b[3]; + int ret=NS_SUCC; + size_t l; + + b[0]=s->escape; + b[1]=c; + b[2]='\0'; + +#ifdef NS_DEBUG + if(c<27) + fprintf(stderr,NS_PREFIX "screen_key: ^%c-^%c %d\n",s->escape+'A'-1,c+'A'-1,c); + else + fprintf(stderr,NS_PREFIX "screen_key: ^%c-%c %d\n",s->escape+'A'-1,c,c); +#endif + + switch(c) { + case NS_SCREEN_CMD: /* send command (statement) to screen server */ + (void)ns_inp_dial((void *)s,"Enter a command to send to the \"screen\" program",64,&i,ns_inp_tab); + if(i) { + ret=ns_screen_xcommand(s,c,i); + (void)ns_parse_screen_cmd(s,i); + free(i); } + break; + case NS_SCREEN_RENAME: /* rename current display */ + i=s->curr->name; + l=strlen(i); + (void)ns_inp_dial(s,"Enter a new name for the current display",12,&i,NULL); + if(i&&*i) { + char *n; + if((n=malloc(strlen(i)+l+1))) { + strcpy(&n[l],i); + while(l) + n[--l]='\x08'; + ret=ns_screen_xcommand(s,c,n); + free(n); } + free(i); } + break; + default: + ret=ns_screen_command(s,b); + } + + return ret; } + + + +/* ns_parse_screenrc -- read the user's screenrc (if we can find it), + parse it (we need to know if she changes the escapes etc.), and + send it to the actually screen + s the session in question + <- error code */ + +int ns_parse_screenrc(_ns_sess *s) { + int fd=-1; + char *rc=NULL; + char _e='\0',_l='\0',*esc=NULL; + + if(s->home) { + struct stat st; + ssize_t rd=0; + + if((fd=open(s->home,0))>=0) { + if(!fstat(fd,&st)) { + if((rc=malloc(st.st_size+1))) { + char *p; + while(((rd=read(fd,rc,st.st_size))<0)&&(errno==EINTR)); + if(rd<0) + goto fail; + rc[rd]='\0'; + + p=rc; + while(*p) { + char *p2=p,*n; + int f=0; + while(*p2&&*p2!='\n'&&*p2!='\r') /* find EOL */ + p2++; + n=p2; + while(*n=='\r'||*n=='\n') /* delete EOL */ + *(n++)='\0'; + while(isspace(*p)) + p++; + + p2=p; /* on first non-white */ + while(*p2) { + if(*p2=='\\') { + p2++; + if(*p2) /* sanity check */ + p2++; } + else { + if(*p2=='\"') + f=1-f; + if(!f&&*p2=='#') /* comment, kill to EOL */ + *p2='\0'; + else + p2++; }} + + if(strlen(p)) /* any commands in line? */ + ns_parse_screen_cmd(s,p); + p=n; /* done, next line */ + } + free(rc); + close(fd); + return NS_SUCC; + } + } + } + } + + fail: + if(fd>=0) + close(fd); + if(rc) + free(rc); + return NS_FAIL; } + + + + /* parse a message (not a display-list) set by the "screen" program screen the session associated with that instance of screen, as returned by ns_attach_by_URL() and related. @@ -926,13 +1638,27 @@ disp_kill(_ns_disp * d3) <- returns an error code. */ int -parse_screen_msg(_ns_sess * screen, char *p) +ns_parse_screen_msg(_ns_sess * screen, char *p) { _ns_efuns *efuns; _ns_disp *disp; - char *p2; - int n, ret = NS_SUCC, type = (strlen(p) > 1) ? NS_SCREEN_STATUS : NS_SCREEN_ST_CLR; + char *p2,*p3,*d; + int ma, mi, mu, + n, ret = NS_SUCC,type; + if(!p) + return NS_FAIL; + + if(*p==':') + p++; + while(isspace(*p)) + p++; + + type = (strlen(p) > 1) ? NS_SCREEN_STATUS : NS_SCREEN_ST_CLR; + + if(type==NS_SCREEN_ST_CLR) { + NS_IF_EFUN_EXISTS(efuns, screen, NULL, err_msg) + ret = efuns->err_msg(NULL, type,""); } /* a screen display can disappear because the program in it dies, or because we explicitly ask screen to kill the display. in the latter case, screen messages upon success. rather than explicitly killing @@ -940,12 +1666,33 @@ parse_screen_msg(_ns_sess * screen, char *p) the status-line checker will notice the disp has gone, and delete it from the struct-list). this way, we won't need to duplicate the delete-logic here. */ - if (sscanf(p, "Window %d (%s) killed.", &n, p2) == 2) { - size_t x = strlen(p2); + else if(!strncmp(p,"Window ",strlen("Window "))&&(p2=strrchr(p,' '))&&!strcmp(p2," killed.")) { +#ifdef NS_DEBUG + fprintf(stderr,NS_PREFIX "ns_parse_screen_msg: window kill detected.\n"); +#endif ret = ns_upd_stat(screen); - } else { /* ignoble message */ + p=NULL; } + else if(!strcmp(p,"New screen...")||!strncmp(p,"msgwait",strlen("msgwait"))||!strncmp(p,"msgminwait",strlen("msgminwait"))) + p=NULL; + else if (sscanf(p, NS_SCREEN_VERSION, &p3, &ma, &mi, &mu, &p2, &d) == 6) { + if(!strcmp("en",p3)) + screen->backend=NS_MODE_SCREEN; + else if(!strcmp("am",p3)) + screen->backend=NS_MODE_SCREAM; +#ifdef NS_DEBUG + fprintf(stderr,NS_PREFIX "ns_parse_screen_msg: scre%s %d.%2d.%2d %s a/o %s\n",p3,ma,mi,mu,p2,d); +#endif + } + else if(!strcmp(p,NS_SCREEN_NO_DEBUG)) + p="debug info was not compiled into \"screen\"..."; + else if(!strncmp(p, NS_SCREEN_DK_CMD, strlen(NS_SCREEN_DK_CMD))) { + p[strlen(p)-1]='\0'; + p2=&p[strlen(NS_SCREEN_DK_CMD)]; + p="unknown screen statement ignored"; + } + if(p) { /* status. send to status-line or dialog or whatever */ NS_IF_EFUN_EXISTS(efuns, screen, NULL, err_msg) - ret = efuns->err_msg(NULL, type, (type == NS_SCREEN_STATUS) ? p : ""); + ret = efuns->err_msg(NULL, type,p); } return ret; } @@ -959,7 +1706,7 @@ parse_screen_msg(_ns_sess * screen, char *p) than can be enumerated in the status-line (we do have workarounds for that case, they're just not very well tested yet). do not touch this unless you are absolutely sure you know what you're - doing. 2002/05/01 Azundris + doing. 2002/05/01 Azundris screen the session associated with that instance of screen, as returned by ns_attach_by_URL() and related. @@ -975,13 +1722,12 @@ parse_screen_msg(_ns_sess * screen, char *p) <- returns an error code. */ int -parse_screen(_ns_sess * screen, int force, int width, char *p) +ns_parse_screen(_ns_sess * screen, int force, int width, char *p) { char *p4, *p3, *p2; /* pointers for parser magic */ static const char *p5 = NS_SCREEN_FLAGS; static size_t l = sizeof(NS_SCREEN_FLAGS); #if (NS_SCREEN_UPD_FREQ>0) - static time_t t = 0; time_t t2 = time(NULL); #endif int ret = NS_SUCC, tmp, status_blanks = 0, /* status-bar overflow? */ @@ -991,19 +1737,12 @@ parse_screen(_ns_sess * screen, int force, int width, char *p) _ns_efuns *efuns; _ns_disp *disp = NULL, *d2 = NULL; - if (!p) + if (!screen||!p||!width) return NS_FAIL; if (!force) return NS_SUCC; -#if (NS_SCREEN_UPD_FREQ>0) - if ((t2 - t) > NS_SCREEN_UPD_FREQ) { - (void) ns_upd_stat(screen); - t = t2; - } -#endif - if (p = strdup(p)) { _ns_parse pd[NS_MAX_DISPS]; p2 = &p[width - 1]; @@ -1013,7 +1752,7 @@ parse_screen(_ns_sess * screen, int force, int width, char *p) } /* p2 now points behind last item */ #ifdef NS_DEBUG - fprintf(stderr, "::%s::\n", p); + fprintf(stderr, NS_PREFIX "parse_screen: screen sends ::%s::\n", p); #endif #ifdef NS_PARANOID_ @@ -1022,7 +1761,7 @@ parse_screen(_ns_sess * screen, int force, int width, char *p) if (disp && !(disp->flags & NS_SCREAM_CURR)) { /* flags need updating */ disp->flags |= NS_SCREAM_CURR; /* set flag to avoid calling inp_text */ ret = ns_upd_stat(screen); - } /* more thn once */ + } /* more than once */ free(p); return ret; } @@ -1072,11 +1811,12 @@ parse_screen(_ns_sess * screen, int force, int width, char *p) p3 = &p3[strlen(p3)]; /* weirdness => skip remainder */ } while (*p3); -#ifdef NS_DEBUG +#ifdef NS_DEBUG_ + fputs(NS_PREFIX "parse_screen: found ",stderr); for (r = 0; r < parsed; r++) if (pd[r].name) - printf("%d(%d/%d,%s) ", r, pd[r].screen, pd[r].real, pd[r].name); - puts("\n"); + fprintf(stderr,"%d(%d/%d,%s) ", r, pd[r].screen, pd[r].real, pd[r].name); + fputs("\n\n",stderr); #endif for (r = 0; r < parsed; r++) { @@ -1085,7 +1825,7 @@ parse_screen(_ns_sess * screen, int force, int width, char *p) if (!disp) { /* new display */ if (!(disp = disp_fetch_or_make(screen, n)) || !(disp->name = strdup(pd[r].name))) { - fprintf(stderr, "out of memory in parse_screen::new_display(%d)\n", n); + fprintf(stderr, NS_PREFIX "parse_screen: out of memory in new_display(%d)\n", n); ret = NS_FAIL; } else { NS_IF_EFUN_EXISTS(efuns, screen, NULL, ins_disp) @@ -1113,7 +1853,7 @@ parse_screen(_ns_sess * screen, int force, int width, char *p) _ns_disp *d3 = disp->prvs, *d4; while (d3 && d3 != d2) { #ifdef NS_DEBUG - fprintf(stderr, "remove expired middle %d \"%s\"...\n", d3->index, d3->name); + fprintf(stderr, NS_PREFIX "parse_screen: remove expired middle %d \"%s\"...\n", d3->index, d3->name); #endif d4 = d3->prvs; NS_IF_EFUN_EXISTS(efuns, screen, NULL, del_disp) @@ -1121,8 +1861,6 @@ parse_screen(_ns_sess * screen, int force, int width, char *p) disp_kill(d3); d3 = d4; } - if (!d2) - ns_upd_stat(screen); } d2 = disp; } @@ -1131,19 +1869,17 @@ parse_screen(_ns_sess * screen, int force, int width, char *p) #ifdef NS_PARANOID if (!r) { -# ifdef NS_DEBUG if (!(err_inhibit & NS_ERR_WEIRDSCREEN)) { - err_inhibit |= NS_ERR_WEIRDSCREEN; - fprintf(stderr, "libscream::parse_screen() d2==NULL\n" + err_inhibit |= NS_ERR_WEIRDSCREEN; + fprintf(stderr, NS_PREFIX "parse_screen: !r\n" "This should never happen. It is assumed that you use a\n" "rather unusual configuration for \"screen\". Please\n" - "send the result of 'screen --version' to \n" + "send the result of 'screen --version' to \n" "(together with your ~/.screenrc and /etc/screenrc if present).\n" "If at all possible, please also run 'Eterm -e screen' and make\n" "a screenshot of the offending window (and the window only, the\n" "beauty of your desktop is not relevant to this investigation. : ).\n"); } -# endif ret = ns_upd_stat(screen); free(p); return NS_FAIL; @@ -1155,7 +1891,7 @@ parse_screen(_ns_sess * screen, int force, int width, char *p) _ns_disp *d3 = disp; for (disp = disp->next; disp;) { #ifdef NS_DEBUG - fprintf(stderr, "remove expired right %d \"%s\"...\n", disp->index, disp->name); + fprintf(stderr, NS_PREFIX "parse_screen: remove expired right %d \"%s\"...\n", disp->index, disp->name); #endif d2 = disp; if (d2->sess->curr == d2) @@ -1170,11 +1906,35 @@ parse_screen(_ns_sess * screen, int force, int width, char *p) } else /* not a list of displays, but a message. handle separately. */ - ret = parse_screen_msg(screen, p); + ret = ns_parse_screen_msg(screen, p); - free(p); + free(p); /* release our (modified) copy of the status-line */ } - /* release our (modified) copy of the status-line */ + + /* send init string the first time around, just to be on the safe side. + we could send it before entering this function for the first time, + but that would break if escapes or screenrc were set from the + command-line. don't ask. */ +#ifdef NS_DEBUG + if(!screen) + fprintf(stderr,NS_PREFIX "parse_screen: session went away!?\n\n CONDITION RED!\n CONDITION RED!\n DANGER WILL ROBINSON!!!\n\n"); + else +#endif + { + if(!screen->timestamp) { + screen->timestamp=time(NULL); +#ifdef NS_DEBUG + fprintf(stderr,NS_PREFIX "parse_screen: sending NS_SCREEN_INIT with prefix %d...\n",screen->escape); +#endif + ret=ns_screen_command(screen,NS_SCREEN_INIT); } +#if (NS_SCREEN_UPD_FREQ>0) + else if ((t2 - screen->timestamp) > NS_SCREEN_UPD_FREQ) { + (void) ns_upd_stat(screen); + screen->timestamp = t2; + } +#endif + } + return ret; } diff --git a/src/menus.c b/src/menus.c index 838acca..00e9103 100644 --- a/src/menus.c +++ b/src/menus.c @@ -45,11 +45,17 @@ static const char cvs_ident[] = "$Id$"; #include "windows.h" menulist_t *menu_list = NULL; -static event_dispatcher_data_t menu_event_data; +#ifndef ESCREEN +static +#endif +event_dispatcher_data_t menu_event_data; static GC topShadowGC, botShadowGC; static Time button_press_time; static int button_press_x = 0, button_press_y = 0; -static menu_t *current_menu; +#ifndef ESCREEN +static +#endif +menu_t *current_menu; static inline void grab_pointer(Window win); static inline void ungrab_pointer(void); @@ -719,6 +725,8 @@ menuitem_delete(menuitem_t *item) FREE(item->action.string); } else if (item->type == MENUITEM_SCRIPT) { FREE(item->action.script); + } else if (item->type == MENUITEM_ALERT) { + FREE(item->action.alert); } if (item->text) { FREE(item->text); @@ -766,6 +774,9 @@ menuitem_set_action(menuitem_t *item, unsigned char type, char *action) case MENUITEM_SCRIPT: item->action.script = STRDUP(action); break; + case MENUITEM_ALERT: + item->action.alert = STRDUP(action); + break; case MENUITEM_STRING: case MENUITEM_ECHO: item->action.string = (char *) MALLOC(strlen(action) + 2); @@ -1206,11 +1217,33 @@ menu_action(menuitem_t *item) cmd_write((unsigned char *) item->action.string, strlen(item->action.string)); break; case MENUITEM_ECHO: +#ifdef ESCREEN + if(TermWin.screen_mode&&TermWin.screen) { /* translate escapes */ +# ifdef NS_DEBUG + { + char *p=item->action.string; + fprintf(stderr,NS_PREFIX "::menu_action: "); + while(*p) { + if(*p<' ') + fprintf(stderr,"^%c",*p-1+'A'); + else + fprintf(stderr,"%c",*p); + p++; + } + fputs("\n",stderr); + } +# endif + (void)ns_screen_command(TermWin.screen,item->action.string); } + else +#endif tt_write((unsigned char *) item->action.string, strlen(item->action.string)); break; case MENUITEM_SCRIPT: script_parse((char *) item->action.script); break; + case MENUITEM_ALERT: + menu_dial(NULL,item->action.alert,0,NULL,NULL); + break; default: fatal_error("Internal Program Error: Unknown menuitem type: %u\n", item->type); break; @@ -1250,3 +1283,156 @@ menu_invoke_by_title(int x, int y, Window win, char *title, Time timestamp) } menu_invoke(x, y, win, menu, timestamp); } + +/* tab completion for screen-commands + xd extra-data (current unused) + sc keywords for tab-completion + nsc entries in sc + !b current entry (changes) + l number of characters to compare in current entry + m maximum number of characters in entry (size of input buffer) + <- error code */ + +int +menu_tab(void *xd,char *sc[],int nsc,char *b,size_t l,size_t m) { + int n,n2=0; + + for(n=0;n=m) /* buffer would overflow => fail */ + return -1; + strcpy(b,sc[n]); + return 0; }}} + + return -1; } + +/* open a dialog. this is a bit of a hack and should really resize otf. + xd extra-data (userdef) for inp_tab + prompt the prompt, obviously. required. + maxlen how long the input may get. 0 for an uneditable alert box. + !retstr the address of a pointer. that actual pointer may be NULL, + or point to a default value for the input. after completion, + the pointer will reference the user's input, or be NULL if + the user cancelled input + inp_tab function doing tab-completion, NULL for none + <- error code (0 succ, -1 fail, -2 cancel) + */ + +int +menu_dial(void *xd, char *prompt, int maxlen, char **retstr,int (*inp_tab)(void *,char *,size_t,size_t)) +{ + static unsigned char short_buf[256]; + unsigned char *kbuf = short_buf; + menu_t *m; + menuitem_t *i; + register int ch; + int f=0,len,ret=-1,tab=0; + XEvent ev; + KeySym keysym; + char *b,*old; + size_t l; + + if(!prompt||!*prompt) + return ret; + + if(!maxlen||!retstr) { + inp_tab=NULL; + maxlen=0; + retstr=NULL; + if((b=strdup("Press \"Return\" to continue..."))==NULL) + return ret; } + else { + if(((b=malloc(maxlen+1))==NULL)) + return ret; + if(*retstr) { + strncpy(b,*retstr,maxlen); + b[maxlen]='\0'; } + else + b[0]='\0'; } + + if((m=menu_create(prompt))) { + for(l=0;lnummenus;l++) { /* copycat font entry to */ + if(menu_list->menus[l]->font) { /* blend in with l&f */ + m->font =menu_list->menus[l]->font; + m->fwidth =menu_list->menus[l]->fwidth; + m->fheight=menu_list->menus[l]->fheight; +#ifdef MULTI_CHARSET + m->fontset=menu_list->menus[l]->fontset; +#endif + break; }} + + if((i=menuitem_create("..."))) { + int h; + old=i->text; + i->text=b; + i->len =strlen(b); + + if(m->font) { /* pre-calc width so we can center the dialog */ + l=strlen(prompt); + if(i->len>l) + l=XTextWidth(m->font,i->text,i->len); + else + l=XTextWidth(m->font,prompt,l); } + else + l=200; + + menuitem_set_action(i,MENUITEM_STRING,"error"); + menu_add_item(m,i); + menu_invoke((int)((TermWin_TotalWidth()-l)/2),(int)(TermWin_TotalHeight()/2)-20,TermWin.parent,m,CurrentTime); + + do { + do { + while(!XPending(Xdisplay)) + ; + XNextEvent(Xdisplay,&ev); + } while(ev.type!=KeyPress); + + len=XLookupString(&ev.xkey,(char *)kbuf,sizeof(short_buf),&keysym,NULL); + ch=kbuf[0]; + l=strlen(b); + + if(ch!='\t') + tab=0; + + if(ch>=' ') { + if(llen=strlen(b); + menu_draw(m); + } while(!f); + + i->text=old; + i->len =strlen(old); + + /* we could just return b, but it might be longer than we need */ + if(retstr) + *retstr=(!maxlen||(f==2))?NULL:strdup(b); + ret=(f==2)?-2:0; + } + m->font=NULL; +#ifdef MULTI_CHARSET + m->fontset=NULL; +#endif + menu_delete(m); + } + free(b); + return ret; +} diff --git a/src/menus.h b/src/menus.h index e173885..5f461ee 100644 --- a/src/menus.h +++ b/src/menus.h @@ -34,6 +34,7 @@ #define MENUITEM_STRING (1UL << 2) #define MENUITEM_ECHO (1UL << 3) #define MENUITEM_SCRIPT (1UL << 4) +#define MENUITEM_ALERT (1UL << 5) #define MENU_STATE_IS_MAPPED (1UL << 0) #define MENU_STATE_IS_CURRENT (1UL << 1) @@ -63,6 +64,7 @@ typedef struct { menu_t *submenu; char *string; char *script; + char *alert; } action; char *text, *rtext; unsigned short len, rlen; @@ -92,6 +94,10 @@ typedef struct { /************ Variables ************/ extern menulist_t *menu_list; +#ifdef ESCREEN +extern event_dispatcher_data_t menu_event_data; +#endif + /************ Function Prototypes ************/ _XFUNCPROTOBEGIN @@ -140,6 +146,8 @@ extern void menu_display(int, int, menu_t *); extern void menu_action(menuitem_t *); extern void menu_invoke(int, int, Window, menu_t *, Time); extern void menu_invoke_by_title(int, int, Window, char *, Time); +extern int menu_tab(void *,char *[],int,char *,size_t,size_t); +extern int menu_dial(void *,char *, int, char **,int (*)(void *,char *,size_t,size_t)); _XFUNCPROTOEND diff --git a/src/options.c b/src/options.c index 0a54e13..978778a 100644 --- a/src/options.c +++ b/src/options.c @@ -138,6 +138,7 @@ char *rs_theme = NULL; char *rs_config_file = NULL; #ifdef ESCREEN char *rs_url = NULL; +char *rs_hop = NULL; #endif unsigned int rs_line_space = 0; unsigned int rs_meta_mod = 0, rs_alt_mod = 0, rs_numlock_mod = 0; @@ -166,6 +167,7 @@ static const struct { OPT_STR('d', "display", "X server to connect to", &display_name), #ifdef ESCREEN OPT_STR('U', "URL", "an URL pointing at a screen-session to pick up", &rs_url), + OPT_STR('Z', "[lclport:]fw[:fwport]", "the destination machine -U can only be seen by the firewall fw. tunnel.", &rs_hop), #endif #if DEBUG <= 0 OPT_ILONG("debug", "level of debugging information to show (support not compiled in)", &DEBUG_LEVEL), diff --git a/src/options.h b/src/options.h index 9366995..1d01a5c 100644 --- a/src/options.h +++ b/src/options.h @@ -136,6 +136,7 @@ extern char *rs_theme; extern char *rs_config_file; #ifdef ESCREEN extern char *rs_url; +extern char *rs_hop; #endif extern unsigned int rs_line_space; extern unsigned int rs_meta_mod, rs_alt_mod, rs_numlock_mod; diff --git a/src/pixmap.c b/src/pixmap.c index 1be71ef..b085fbf 100644 --- a/src/pixmap.c +++ b/src/pixmap.c @@ -1066,7 +1066,7 @@ render_simage(simage_t *simg, Window win, unsigned short width, unsigned short h XSetClipMask(Xdisplay, gc, mask); XSetClipOrigin(Xdisplay, gc, 0, 0); } - if (simg->pmap->pixmap) { + if (simg->pmap->pixmap != None) { LIBAST_X_FREE_PIXMAP(simg->pmap->pixmap); simg->pmap->pixmap = None; } @@ -1275,7 +1275,7 @@ render_simage(simage_t *simg, Window win, unsigned short width, unsigned short h XSetWindowBackgroundPixmap(Xdisplay, win, buffer_pixmap); } else { if ((renderop & RENDER_FORCE_PIXMAP) || (simg->iml->bevel != NULL)) { - if (simg->pmap->pixmap) { + if (simg->pmap->pixmap != None) { LIBAST_X_FREE_PIXMAP(simg->pmap->pixmap); } simg->pmap->pixmap = LIBAST_X_CREATE_PIXMAP(width, height); @@ -2118,7 +2118,6 @@ get_desktop_pixmap(void) void free_desktop_pixmap(void) { - if (desktop_pixmap_is_mine && desktop_pixmap != None) { LIBAST_X_FREE_PIXMAP(desktop_pixmap); desktop_pixmap_is_mine = 0; diff --git a/src/scream.h b/src/scream.h index 3bab209..223bab4 100644 --- a/src/scream.h +++ b/src/scream.h @@ -1,7 +1,7 @@ /**************************************************************************** * scream::scream.h * routines to connect to screen and or scream daemons. - * GNU Public Licence applies. + * BSD Licence applies. * 2002/04/19 Azundris incept ***************************************************************************/ @@ -32,6 +32,7 @@ #define NS_SCREEN_STATUS 11 #define NS_SCREEN_ST_CLR 12 #define NS_EFUN_NOT_SET 13 +#define NS_USER_CXL 14 #define NS_ERR_WEIRDSCREEN 1 @@ -46,29 +47,51 @@ #define NS_SCREAM_MASK (~(NS_SCREAM_UTMP|NS_SCREAM_PRVS)) +#define NS_HOP_DOWN 0 +#define NS_HOP_UP 1 + /***************************************************************************/ -typedef struct __ns_sess { /* a whole screen-session with many clients */ - int where; /* local/remote */ - int backend; /* screen/scream */ - int nesting; /* 0=topLevel, 1=screen within a screen etc */ - char *proto; /* protocol. usually "screen" */ - char *host; /* host. numeric or symbolic. often "localhost" */ - int port; /* port. usually TCP22: SSH */ - char *user; /* user. often current local user */ - char *pass; /* password. used for su. for remote sessions, a - ssh-key should be placed on the remote machine. */ - char *rsrc; /* additional parameter to screen/scream. URL-enc */ - void *userdef; /* the term-app can store a pointer here */ - int fd; /* fd for communication */ - char escape,literal; /* talking to screen: defaults to ^A, a */ +typedef struct __ns_hop { + int localport; + char *fw; + int fwport; + int established; + int delay; + int refcount; + struct __ns_sess *sess; /* first only, others have same host/port */ + struct __ns_hop *next; +} _ns_hop; + + + +typedef struct __ns_sess { /* a whole screen-session with many clients */ + int where; /* local/remote */ + int backend; /* screen/scream */ + int nesting; /* 0=topLevel, 1=screen within a screen etc */ + time_t timestamp; /* last updated when? see NS_SCREEN_UPD_FREQ */ + char *proto; /* protocol. usually "screen" */ + char *host; /* host. numeric or symbolic. ("localhost") */ + int port; /* port. usually TCP22: SSH */ + char *user; /* user. often current local user */ + char *pass; /* password. used for su. for remote sessions, a + ssh-key should be on the remote machine. */ + char *rsrc; /* add'l parameter to screen/scream. URL-enc */ + char *home; /* user's home dir. so we can find .screenrc */ + void *userdef; /* the term-app can store a pointer here */ + int fd; /* fd for communication */ + char escape,literal; /* talking to screen: defaults to ^A, a */ + int dsbb; /* default length of scroll-back buffer */ struct __ns_efuns *efuns; /* callbacks into the terminal program. */ + struct __ns_hop *hop; /* tunnel, if any */ struct __ns_disp *dsps; /* first display (that with the lowest index) */ struct __ns_disp *curr; /* current display (NULL for none) */ + struct __ns_sess *prvs; /* previous session in session list */ + struct __ns_sess *next; /* next session in session list */ } _ns_sess; @@ -105,6 +128,8 @@ typedef struct __ns_efuns { /* callbacks into the terminal program */ int (*err_msg)(void *,int,char *); int (*execute)(void *,char **); int (*inp_text)(void *,int,char *); + int (*inp_dial)(void *,char *,int,char **,int (*)(void *,char *,size_t,size_t)); + int (*inp_tab)(void *,char *[],int,char *,size_t,size_t); } _ns_efuns; @@ -128,6 +153,7 @@ _ns_efuns *ns_dst_efuns(_ns_efuns **); _ns_efuns *ns_get_efuns(_ns_sess *,_ns_disp *); /* debug */ +void ns_desc_hop(_ns_hop *,char *); void ns_desc_sess(_ns_sess *,char *); /* convenience */ @@ -135,11 +161,32 @@ _ns_disp *disp_fetch_or_make(_ns_sess *,int); /* transparent attach/detach */ _ns_sess *ns_attach_by_sess(_ns_sess **,int *); -_ns_sess *ns_attach_by_URL(char *,_ns_efuns **,int *,void *); +_ns_sess *ns_attach_by_URL(char *,char *,_ns_efuns **,int *,void *); + + +/* send command to screen */ +int ns_screen_command(_ns_sess *, char *); + +/* send statement to screen */ +int ns_screen_xcommand(_ns_sess *,char , char *); + +/* parse and forward a screen-statement */ +int ns_parse_screen_cmd(_ns_sess *,char *); + +/* parse and forward a screen-hotkey */ +int ns_parse_screen_key(_ns_sess *,char); + +/* parse screen escape setup */ +char ns_parse_esc(char **); + +/* init session (read .screenrc, or whatnot) */ +int ns_sess_init(_ns_sess *); /* what the terminal should call the last line -- screen's "hardstatus" changes. submit session, terminal-width, and a pointer to said line. */ -int parse_screen(_ns_sess *,int,int,char *); +int ns_parse_screen(_ns_sess *,int,int,char *); + + /* things the term might ask screen/scream to do ***************************/ int ns_scroll2x(_ns_sess *,int); @@ -151,6 +198,9 @@ int ns_rem_disp(_ns_sess *,int); int ns_ren_disp(_ns_sess *,int,char *); int ns_log_disp(_ns_sess *,int,char *); int ns_upd_stat(_ns_sess *); +int ns_inp_dial(_ns_sess *,char *,int,char **,int (*)(void *,char *,size_t,size_t)); + + /* register efuns (callbacks) **********************************************/ void ns_register_ssx(_ns_efuns *,int (*set_scroll_x)(void *,int)); @@ -170,6 +220,9 @@ void ns_register_err(_ns_efuns *,int (*err_msg)(void *,int,char *)); void ns_register_exe(_ns_efuns *,int (*execute)(void *,char **)); void ns_register_txt(_ns_efuns *,int (*inp_text)(void *,int,char *)); +void ns_register_inp(_ns_efuns *,int (*)(void *,char *,int,char **,int (*)(void *,char *,size_t,size_t))); +void ns_register_tab(_ns_efuns *,int (*)(void *,char *[],int,char *,size_t,size_t)); + /***************************************************************************/ diff --git a/src/screamcfg.h b/src/screamcfg.h index 404d4f2..71359d6 100644 --- a/src/screamcfg.h +++ b/src/screamcfg.h @@ -2,7 +2,7 @@ * scream::screamcfg.h * user-tunable parameters for the routines to connect to screen and/or * scream daemons. - * GNU Public Licence applies. + * BSD Licence applies. * 2002/04/19 Azundris incept ***************************************************************************/ @@ -16,40 +16,62 @@ #define NS_PARANOID /* define NS_DEBUG to get debug-info. no support for those who undef this. */ -#undef NS_DEBUG +#undef NS_DEBUG /* debug memory stuff. never actually used this. */ #undef NS_DEBUG_MEM -#define NS_SSH_CALL "ssh" -#define NS_SSH_OPTS "-t" -#define NS_SCREAM_CALL "scream" -#define NS_SCREAM_OPTS "-xRR" -#define NS_SCREEN_CALL "screen" -#define NS_SCREEN_OPTS "-c /dev/null -xRR" -#define NS_SCREEM_CALL "\"" NS_SCREAM_CALL " " NS_SCREAM_OPTS " 2>/dev/null || " NS_SCREEN_CALL " " NS_SCREEN_OPTS "\"" +#define NS_SSH_CALL "ssh" +#define NS_SSH_OPTS "-t" +#define NS_SSH_TUNNEL_OPTS "-N" +#define NS_SCREAM_CALL "scream" +#define NS_SCREAM_OPTS "-xRR" +#define NS_SCREEN_CALL "screen" +#define NS_SCREEN_OPTS "-xRR" +#define NS_SCREEM_CALL "\"" NS_SCREAM_CALL " " NS_SCREAM_OPTS " 2>/dev/null || " NS_SCREEN_CALL " " NS_SCREEN_OPTS "\"" +#define NS_SCREEN_RC ".screenrc" /* this should never change. the escape-char for an out-of-the-box "screen". don't change this just because you set something else in your .screenrc */ -#define NS_SCREEN_ESCAPE '\x01' -#define NS_SCREEN_LITERAL 'a' +#define NS_SCREEN_ESCAPE '\x01' +#define NS_SCREEN_LITERAL 'a' +#define NS_SCREEN_CMD ':' +#define NS_SCREEN_RENAME 'A' +#define NS_SCREEN_KILL 'k' +#define NS_SCREEN_DEFSBB 100 /* the following must use the char defined in NS_SCREEN_ESCAPE. if something else is used in the session, libscream will convert it on the fly. */ /* DO NOT use \005Lw for your status, it breaks older screens!! */ -#define NS_SCREEN_UPDATE "\x01w" -#define NS_SCREEN_INIT "\x01:hardstatus lastline\r\x01:defhstatus \"\\005w\"\r\x01:hstatus \"\\005w\"\r" NS_SCREEN_UPDATE +#define NS_SCREEN_UPDATE "\x01w" +#define NS_SCREEN_INIT "\x01:hardstatus lastline\r\x01:defhstatus \"\\005w\"\r\x01:hstatus \"\\005w\"\r\x01:msgminwait 0\r\x01:msgwait 1\r\x01:nethack off\r" NS_SCREEN_UPDATE -#define NS_DFLT_SSH_PORT 22 -#define NS_MAX_PORT 65535 +#define NS_DFLT_SSH_PORT 22 +#define NS_MIN_PORT 1025 +#define NS_MAX_PORT 65535 -#define NS_MAX_DISPS 512 +#define NS_MAX_DISPS 512 -#define NS_SCREEN_FLAGS "*-$!@L&Z" +#define NS_SCREEN_FLAGS "*-$!@L&Z" + +#define NS_SCREEN_DK_CMD "unknown command '" +#define NS_SCREEN_VERSION "scre%s %d.%d.%d %s %s" +#define NS_SCREEN_NO_DEBUG "Sorry, screen was compiled without -DDEBUG option." /* if >0, force an update every NS_SCREEN_UPD_FREQ seconds. a bit of a last resort. */ -#define NS_SCREEN_UPD_FREQ 0 +#define NS_SCREEN_UPD_FREQ 0 + +/* how many seconds to wait for an SSH-tunnel to build when using the + -Z option (tunnel through firewall). 2 for very fast networks, + much more for slow connections. */ +#define NS_TUNNEL_DELAY 3 + +/* what to call the menu entry for Escreen */ +#define NS_MENU_TITLE "Escreen" + +/* prefix for debug info */ +#define NS_PREFIX "libscream::" diff --git a/src/screen.c b/src/screen.c index 364965f..682c97a 100644 --- a/src/screen.c +++ b/src/screen.c @@ -3366,7 +3366,7 @@ xim_get_position(XPoint * pos) void parse_screen_status_if_necessary(void) { - parse_screen(TermWin.screen, (TermWin.screen_pending > 1), TermWin.ncol, screen.text[TermWin.nrow + TermWin.saveLines - 1]); + ns_parse_screen(TermWin.screen, (TermWin.screen_pending > 1), TermWin.ncol, screen.text[TermWin.nrow + TermWin.saveLines - 1]); if (TermWin.screen_pending > 1) TermWin.screen_pending = 0; } diff --git a/src/term.c b/src/term.c index 80204f6..4f4a961 100644 --- a/src/term.c +++ b/src/term.c @@ -48,6 +48,9 @@ static const char cvs_ident[] = "$Id$"; #include "scrollbar.h" #include "term.h" #include "windows.h" +#ifdef ESCREEN +# include "screamcfg.h" +#endif #ifdef META8_OPTION unsigned char meta_char = 033; /* Alt-key prefix */ @@ -191,7 +194,9 @@ get_modifiers(void) void lookup_key(XEvent * ev) { - +#ifdef ESCREEN + static int escreen_escape = 0; +#endif static int numlock_state = 0; int ctrl, meta, shft, len; KeySym keysym; @@ -256,6 +261,21 @@ lookup_key(XEvent * ev) } #endif /* USE_XIM */ +#ifdef ESCREEN + if (escreen_escape) { + if(kbuf[0]) { + escreen_escape=0; + if(kbuf[0]<128) + (void)ns_parse_screen_key(TermWin.screen,kbuf[0]); + LK_RET(); + } + } + else if (TermWin.screen&&TermWin.screen->escape==kbuf[0]) { + escreen_escape=1; + LK_RET(); + } +#endif + #ifdef USE_XIM /* Don't do anything without a valid keysym. */ if (valid_keysym) { @@ -371,6 +391,7 @@ lookup_key(XEvent * ev) } #endif + switch (keysym) { case XK_Print: /* Print the screen contents out to the print pipe */ #if DEBUG >= DEBUG_SELECTION