/* Copyright (c) 1993 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) * Copyright (c) 1987 Oliver Laumann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file COPYING); if not, write to the * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * **************************************************************** */ #include "rcs.h" RCS_ID("$Id: process.c,v 1.1.1.1 1993/08/15 18:32:57 nate Exp $ FAU") #include #include #include #include #if !defined(sun) && !defined(B43) && !defined(ISC) && !defined(pyr) && !defined(_CX_UX) # include #endif #include #ifndef sun #include #endif #include "config.h" #include "screen.h" #include "extern.h" #if defined(sun) && defined(SVR4) # include #endif extern struct comm comms[]; extern char *rc_name; extern char *RcFileName, *home, *extra_incap, *extra_outcap; extern char *BellString, *ActivityString, *ShellProg, *ShellArgs[]; extern char *hardcopydir, *screenlogdir; extern char *VisualBellString; extern int VBellWait, MsgWait, MsgMinWait, SilenceWait; extern char SockPath[], *SockNamePtr; extern int TtyMode, auto_detach; extern int iflag; extern int default_wrap; extern int use_hardstatus, visual_bell, default_monitor; extern int default_startup; extern int slowpaste, defobuflimit; extern int ZombieKey; #ifdef AUTO_NUKE extern int defautonuke; #endif extern int intrc, origintrc; /* display? */ extern struct NewWindow nwin_default, nwin_undef; #ifdef COPY_PASTE extern int join_with_cr; extern char mark_key_tab[]; extern char *BufferFile; #endif #ifdef POW_DETACH extern char *BufferFile, *PowDetachString; #endif extern time_t Now; static int CheckArgNum __P((int, char **)); static void FreeKey __P((int)); static int NextWindow __P((void)); static int PreviousWindow __P((void)); static int MoreWindows __P((void)); static void LogToggle __P((int)); static void ShowTime __P((void)); static void ShowInfo __P((void)); static void SwitchWindow __P((int)); static char **SaveArgs __P((char **)); static struct win *WindowByName __P((char *)); static int WindowByNumber __P((char *)); static void DoAction __P((struct action *, int)); static int ParseSwitch __P((struct action *, int *)); static int ParseOnOff __P((struct action *, int *)); static int ParseSaveStr __P((struct action *act, char **)); static int ParseNum __P((struct action *act, int *)); static int ParseWinNum __P((struct action *act, int *)); static int ParseOct __P((struct action *act, int *)); static char *ParseChar __P((char *, char *)); static int IsNum __P((char *, int)); static int IsNumColon __P((char *, int, char *, int)); static void InputColon __P((void)); static void Colonfin __P((char *, int)); static void InputSelect __P((void)); static void InputSetenv __P((char *)); static void InputAKA __P((void)); static void AKAfin __P((char *, int)); #ifdef COPY_PASTE static void copy_reg_fn __P((char *, int)); static void ins_reg_fn __P((char *, int)); #endif static void process_fn __P((char *, int)); #ifdef PASSWORD static void pass1 __P((char *, int)); static void pass2 __P((char *, int)); #endif #ifdef POW_DETACH static void pow_detach_fn __P((char *, int)); #endif extern struct display *display, *displays; extern struct win *fore, *console_window, *windows; extern struct user *users; extern char screenterm[], HostName[], version[]; extern struct NewWindow nwin_undef, nwin_default; extern struct LayFuncs WinLf; extern struct layer BlankLayer; extern int Z0width, Z1width; extern int real_uid, real_gid; #ifdef NETHACK extern int nethackflag; #endif struct win *wtab[MAXWIN]; /* window table */ struct action ktab[256]; /* command key translation table */ #ifdef MULTIUSER extern char *multi; #endif #ifdef PASSWORD int CheckPassword; char Password[20]; #endif struct plop plop_tab[MAX_PLOP_DEFS]; #ifdef PTYMODE int TtyMode = PTYMODE; #else int TtyMode = 0622; #endif int hardcopy_append = 0; int all_norefresh = 0; char *noargs[1]; void InitKeytab() { register unsigned int i; for (i = 0; i < sizeof(ktab)/sizeof(*ktab); i++) { ktab[i].nr = RC_ILLEGAL; ktab[i].args = noargs; } ktab['h'].nr = RC_HARDCOPY; #ifdef BSDJOBS ktab['z'].nr = ktab[Ctrl('z')].nr = RC_SUSPEND; #endif ktab['c'].nr = ktab[Ctrl('c')].nr = RC_SCREEN; ktab[' '].nr = ktab[Ctrl(' ')].nr = ktab['n'].nr = ktab[Ctrl('n')].nr = RC_NEXT; ktab['N'].nr = RC_NUMBER; ktab[Ctrl('h')].nr = ktab[0177].nr = ktab['p'].nr = ktab[Ctrl('p')].nr = RC_PREV; ktab['k'].nr = ktab[Ctrl('k')].nr = RC_KILL; ktab['l'].nr = ktab[Ctrl('l')].nr = RC_REDISPLAY; ktab['w'].nr = ktab[Ctrl('w')].nr = RC_WINDOWS; ktab['v'].nr = ktab[Ctrl('v')].nr = RC_VERSION; ktab['q'].nr = ktab[Ctrl('q')].nr = RC_XON; ktab['s'].nr = ktab[Ctrl('s')].nr = RC_XOFF; ktab['t'].nr = ktab[Ctrl('t')].nr = RC_TIME; ktab['i'].nr = ktab[Ctrl('i')].nr = RC_INFO; ktab['m'].nr = ktab[Ctrl('m')].nr = RC_LASTMSG; ktab['A'].nr = RC_TITLE; #if defined(UTMPOK) && defined(LOGOUTOK) ktab['L'].nr = RC_LOGIN; #endif ktab[','].nr = RC_LICENSE; ktab['W'].nr = RC_WIDTH; ktab['.'].nr = RC_DUMPTERMCAP; ktab[Ctrl('\\')].nr = RC_QUIT; ktab['d'].nr = ktab[Ctrl('d')].nr = RC_DETACH; ktab['r'].nr = ktab[Ctrl('r')].nr = RC_WRAP; ktab['f'].nr = ktab[Ctrl('f')].nr = RC_FLOW; ktab['C'].nr = RC_CLEAR; ktab['Z'].nr = RC_RESET; ktab['H'].nr = RC_LOG; ktab[(int)(unsigned char)DefaultEsc].nr = RC_OTHER; ktab[(int)(unsigned char)DefaultMetaEsc].nr = RC_META; ktab['M'].nr = RC_MONITOR; ktab['?'].nr = RC_HELP; for (i = 0; i < ((MAXWIN < 10) ? MAXWIN : 10); i++) { char *args[2], arg1[10]; args[0] = arg1; args[1] = 0; sprintf(arg1, "%d", i); ktab['0' + i].nr = RC_SELECT; ktab['0' + i].args = SaveArgs(args); } ktab[Ctrl('G')].nr = RC_VBELL; ktab[':'].nr = RC_COLON; #ifdef COPY_PASTE ktab['['].nr = ktab[Ctrl('[')].nr = RC_COPY; ktab[']'].nr = ktab[Ctrl(']')].nr = RC_PASTE; ktab['{'].nr = RC_HISTORY; ktab['}'].nr = RC_HISTORY; ktab['>'].nr = RC_WRITEBUF; ktab['<'].nr = RC_READBUF; ktab['='].nr = RC_REMOVEBUF; ktab['\''].nr = ktab['"'].nr = RC_SELECT; /* calling a window by name */ #endif #ifdef POW_DETACH ktab['D'].nr = RC_POW_DETACH; #endif #ifdef LOCK ktab['x'].nr = ktab[Ctrl('x')].nr = RC_LOCKSCREEN; #endif ktab['b'].nr = ktab[Ctrl('b')].nr = RC_BREAK; ktab['B'].nr = RC_POW_BREAK; ktab['_'].nr = RC_SILENCE; } static void FreeKey(key) int key; { char **p; struct action *act = &ktab[key]; if (act->nr == RC_ILLEGAL) return; act->nr = RC_ILLEGAL; if (act->args == noargs) return; for (p = act->args; *p; p++) free(*p); free(act->args); act->args = noargs; } void ProcessInput(ibuf, ilen) char *ibuf; int ilen; { char *s; int slen; while (display) { fore = d_fore; slen = ilen; s = ibuf; while (ilen > 0) { if (*s++ == d_user->u_Esc) break; ilen--; } slen -= ilen; while (slen) Process(&ibuf, &slen); if (--ilen == 0) d_ESCseen = 1; if (ilen <= 0) return; DoAction(&ktab[(int)(unsigned char)*s], (int)(unsigned char)*s); ibuf = s + 1; ilen--; } } int FindCommnr(str) char *str; { int x, m, l = 0, r = RC_LAST; while (l <= r) { m = (l + r) / 2; x = strcmp(str, comms[m].name); if (x > 0) l = m + 1; else if (x < 0) r = m - 1; else return m; } return RC_ILLEGAL; } static int CheckArgNum(nr, args) int nr; char **args; { int i, n; static char *argss[] = {"no", "one", "two", "three"}; n = comms[nr].flags & ARGS_MASK; for (i = 0; args[i]; i++) ; if (comms[nr].flags & ARGS_ORMORE) { if (i < n) { Msg(0, "%s: %s: at least %s argument%s required", rc_name, comms[nr].name, argss[n], n != 1 ? "s" : ""); return -1; } } else if ((comms[nr].flags & ARGS_PLUSONE) && (comms[nr].flags & ARGS_PLUSTWO)) { if (i != n && i != n + 1 && i != n + 2) { Msg(0, "%s: %s: %s, %s or %s argument%s required", rc_name, comms[nr].name, argss[n], argss[n + 1], argss[n + 2], n != 0 ? "s" : ""); return -1; } } else if (comms[nr].flags & ARGS_PLUSONE) { if (i != n && i != n + 1) { Msg(0, "%s: %s: %s or %s argument%s required", rc_name, comms[nr].name, argss[n], argss[n + 1], n != 0 ? "s" : ""); return -1; } } else if (comms[nr].flags & ARGS_PLUSTWO) { if (i != n && i != n + 2) { Msg(0, "%s: %s: %s or %s argument%s required", rc_name, comms[nr].name, argss[n], argss[n + 2], n != 0 ? "s" : ""); return -1; } } else if (i != n) { Msg(0, "%s: %s: %s argument%s required", rc_name, comms[nr].name, argss[n], n != 1 ? "s" : ""); return -1; } return 0; } /*ARGSUSED*/ static void DoAction(act, key) struct action *act; int key; { int nr = act->nr; char **args = act->args; struct win *p; int i, n, msgok; char *s; char ch; if (nr == RC_ILLEGAL) { debug1("key '%c': No action\n", key); return; } n = comms[nr].flags; if ((n & NEED_DISPLAY) && display == 0) { Msg(0, "%s: %s: display required", rc_name, comms[nr].name); return; } if ((n & NEED_FORE) && fore == 0) { Msg(0, "%s: %s: window required", rc_name, comms[nr].name); return; } if (CheckArgNum(nr, args)) return; #ifdef MULTIUSER if (multi && display) { if (AclCheckPermCmd(d_user, ACL_EXEC, &comms[nr])) return; } #endif /* MULTIUSER */ msgok = display && !*rc_name; switch(nr) { case RC_SELECT: if (!*args) InputSelect(); else if (ParseWinNum(act, &n) == 0) SwitchWindow(n); break; #ifdef AUTO_NUKE case RC_DEFAUTONUKE: if (ParseOnOff(act, &defautonuke) == 0 && msgok) Msg(0, "Default autonuke turned %s", defautonuke ? "on" : "off"); if (display && *rc_name) d_auto_nuke = defautonuke; break; case RC_AUTONUKE: if (ParseOnOff(act, &d_auto_nuke) == 0 && msgok) Msg(0, "Autonuke turned %s", d_auto_nuke ? "on" : "off"); break; #endif case RC_DUPLICATE: if (!*args) { if (fore->w_dupto >= 0) Msg(0, "Duplicating output to window %d", fore->w_dupto); else Msg(0, "No duplicate from here\n"); break; } if (!strcmp(*args, "off")) { fore->w_dupto = -1; break; } while (*args) { n = WindowByNoN(*args++); if (n < 0) { Msg(0, "Invalid window description"); continue; } if ((p = wtab[n]) == 0) { Msg(0, "Window %d does not exist", n); continue; } for (nr = fore->w_number; wtab[nr] && wtab[nr]->w_dupto >= 0;nr = wtab[nr]->w_dupto) { if (wtab[nr]->w_dupto == n) { Msg(0, "Cyclic dup detected\n"); return; } } wtab[n]->w_dupto = fore->w_number; } break; case RC_DEFOBUFLIMIT: if (ParseNum(act, &defobuflimit) == 0 && msgok) Msg(0, "Default limit set to %d", defobuflimit); if (display && *rc_name) d_obufmax = defobuflimit; break; case RC_OBUFLIMIT: if (*args == 0) Msg(0, "Limit is %d, current buffer size is %d", d_obufmax, d_obuflen); else if (ParseNum(act, &d_obufmax) == 0 && msgok) Msg(0, "Limit set to %d", d_obufmax); break; case RC_DUMPTERMCAP: WriteFile(DUMP_TERMCAP); break; case RC_HARDCOPY: WriteFile(DUMP_HARDCOPY); break; case RC_LOG: n = fore->w_logfp ? 1 : 0; ParseSwitch(act, &n); LogToggle(n); break; #ifdef BSDJOBS case RC_SUSPEND: Detach(D_STOP); break; #endif case RC_NEXT: if (MoreWindows()) SwitchWindow(NextWindow()); break; case RC_PREV: if (MoreWindows()) SwitchWindow(PreviousWindow()); break; case RC_KILL: { char *name; n = fore->w_number; #ifdef PSEUDOS if (fore->w_pwin) { FreePseudowin(fore); #ifdef NETHACK if (nethackflag) Msg(0, "You have a sad feeling for a moment..."); else #endif Msg(0, "Filter removed."); break; } #endif name = SaveStr(fore->w_title); KillWindow(fore); #ifdef NETHACK if (nethackflag) Msg(0, "You destroy poor window %d (%s).", n, name); else #endif Msg(0, "Window %d (%s) killed.", n, name); if (name) free(name); break; } case RC_QUIT: Finit(0); /* NOTREACHED */ case RC_DETACH: Detach(D_DETACH); break; #ifdef POW_DETACH case RC_POW_DETACH: if (key >= 0) { static char buf[2]; buf[0] = key; Input(buf, 1, pow_detach_fn, INP_RAW); } else Detach(D_POWER); /* detach and kill Attacher's parent */ break; #endif case RC_DEBUG: #ifdef DEBUG if (!*args) { if (dfp) Msg(0, "debugging info is written to %s/", DEBUGDIR); else Msg(0, "debugging is currently off. Use 'debug on' to enable."); break; } if (dfp) { debug("debug: closing debug file.\n"); fflush(dfp); fclose(dfp); dfp = NULL; } if (strcmp("off", *args)) { char buf[255]; sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, getpid()); if ((dfp = fopen(buf, "a")) == NULL) dfp = stderr; debug("debug: opening debug file.\n"); } #else Msg(0, "Sorry, screen was compiled without -DDEBUG option."); #endif break; case RC_ZOMBIE: if (!(s = *args)) { ZombieKey = 0; break; } if (!(s = ParseChar(s, &ch)) || *s) { Msg(0, "%s:zombie: one character expected.", rc_name); break; } ZombieKey = ch; break; case RC_WALL: for (n = 0, s = *args; args[n]; n++) { /* glue the vector together again. Brute force method. */ while (*s) s++; while (s < args[n+1]) *s++ = ' '; } #ifdef MULTIUSER s = d_user->u_name; #else s = d_usertty; #endif display = NULL; /* a message without display will cause a broadcast */ Msg(0, "%s: %s", s, *args); break; case RC_AT: #ifdef MULTIUSER s = SaveStr(d_user->u_name); #else s = SaveStr(d_usertty); #endif n = strlen(args[0]); if (n) n--; /* * the windows/displays loops are quite dangerous here, take extra * care not to trigger landmines. Things may appear/disappear while * we are walking along. */ switch (args[0][n]) { case '*': { struct display *nd; struct user *u; args[0][n] = '\0'; if (!*args[0]) u = d_user; else for (u = users; u; u = u->u_next) if (!strncmp(s, u->u_name, n)) break; debug1("at all displays of user %s\n", u->u_name); for (display = displays; display; display = nd) { nd = display->_d_next; fore = d_fore; if (d_user != u) continue; debug1("AT display %s\n", d_usertty); DoCommand(args + 1); if (display) Msg(0, "command from %s: %s %s", s, args[1], args[2] ? args[2] : ""); display = NULL; fore = NULL; } free(s); return; } case '%': { struct display *nd; args[0][n] = '\0'; debug1("at display matching '%s'\n", args[0]); for (display = displays; display; display = nd) { nd = display->_d_next; fore = d_fore; if (strncmp(args[0], d_usertty, n) && (strncmp("/dev/", d_usertty, 5) || strncmp(args[0], d_usertty + 5, n)) && (strncmp("/dev/tty", d_usertty, 8) || strncmp(args[0], d_usertty + 8, n))) continue; debug1("AT display %s\n", d_usertty); DoCommand(args + 1); if (display) Msg(0, "command from %s: %s %s", s, args[1], args[2] ? args[2] : ""); display = NULL; fore = NULL; } free(s); return; } case '#': args[0][n--] = '\0'; /* FALLTHROUGH */ default: { struct win *nw; n++; if (!*args[0] || (i = WindowByNumber(args[0])) < 0) { /* try looping over titles */ for (fore = windows; fore; fore = nw) { nw = fore->w_next; if (strncmp(args[0], fore->w_title, n)) continue; debug2("AT window %d(%s)\n", fore->w_number, fore->w_title); i++; DoCommand(args + 1); if ((display = fore->w_display)) Msg(0, "command from %s: %s %s", s, args[1], args[2] ? args[2] : ""); } display = NULL; fore = NULL; if (i < 0) Msg(0, "%s: at '%s': no such window.\n", rc_name, args[0]); free(s); return; } else if (i < MAXWIN && (fore = wtab[i])) { debug2("AT window %d (%s)\n", fore->w_number, fore->w_title); DoCommand(args + 1); if ((display = fore->w_display)) Msg(0, "command from %s: %s %s", s, args[1], args[2] ? args[2] : ""); display = NULL; fore = NULL; free(s); return; } } } Msg(0, "%s: at [identifier][%%|*|#] command [args]", rc_name); free(s); break; #ifdef COPY_PASTE case RC_COPY_REG: if ((s = *args) == NULL) { Input("Copy to register:", 1, copy_reg_fn, INP_RAW); break; } if ((s = ParseChar(s, &ch)) == NULL || *s) { Msg(0, "%s: copy_reg: character, ^x, or (octal) \\032 expected.", rc_name); break; } copy_reg_fn(&ch, 0); break; case RC_INS_REG: if ((s = *args) == NULL) { Input("Insert from register:", 1, ins_reg_fn, INP_RAW); break; } if ((s = ParseChar(s, &ch)) == NULL || *s) { Msg(0, "%s: ins_reg: character, ^x, or (octal) \\032 expected.", rc_name); break; } ins_reg_fn(&ch, 0); break; #endif case RC_REGISTER: if ((s = ParseChar(*args, &ch)) == NULL || *s) Msg(0, "%s: register: character, ^x, or (octal) \\032 expected.", rc_name); else { struct plop *plp = plop_tab + (int)(unsigned char)ch; if (plp->buf) free(plp->buf); plp->buf = SaveStr(expand_vars(args[1])); plp->len = strlen(plp->buf); } break; case RC_PROCESS: if ((s = *args) == NULL) { Input("Process register:", 1, process_fn, INP_RAW); break; } if ((s = ParseChar(s, &ch)) == NULL || *s) { Msg(0, "%s: process: character, ^x, or (octal) \\032 expected.", rc_name); break; } process_fn(&ch, 0); break; case RC_REDISPLAY: Activate(-1); break; case RC_WINDOWS: ShowWindows(); break; case RC_VERSION: Msg(0, "screen %s", version); break; case RC_TIME: ShowTime(); break; case RC_INFO: ShowInfo(); break; case RC_OTHER: if (MoreWindows()) SwitchWindow(d_other ? d_other->w_number : NextWindow()); break; case RC_META: ch = d_user->u_Esc; s = &ch; n = 1; Process(&s, &n); break; case RC_XON: ch = Ctrl('q'); s = &ch; n = 1; Process(&s, &n); break; case RC_XOFF: ch = Ctrl('s'); s = &ch; n = 1; Process(&s, &n); break; case RC_POW_BREAK: case RC_BREAK: n = 0; if (*args && ParseNum(act, &n)) break; SendBreak(fore, n, nr == RC_POW_BREAK); break; #ifdef LOCK case RC_LOCKSCREEN: Detach(D_LOCK); break; #endif case RC_WIDTH: if (*args) { if (ParseNum(act, &n)) break; } else { if (d_width == Z0width) n = Z1width; else if (d_width == Z1width) n = Z0width; else if (d_width > (Z0width + Z1width) / 2) n = Z0width; else n = Z1width; } if (n <= 0) { Msg(0, "Illegal width"); break; } if (n == d_width) break; if (ResizeDisplay(n, d_height) == 0) { DoResize(d_width, d_height); Activate(d_fore ? d_fore->w_norefresh : 0); } else Msg(0, "Your termcap does not specify how to change the terminal's width to %d.", n); break; case RC_HEIGHT: if (*args) { if (ParseNum(act, &n)) break; } else { #define H0height 42 #define H1height 24 if (d_height == H0height) n = H1height; else if (d_height == H1height) n = H0height; else if (d_height > (H0height + H1height) / 2) n = H0height; else n = H1height; } if (n <= 0) { Msg(0, "Illegal height"); break; } if (n == d_height) break; if (ResizeDisplay(d_width, n) == 0) { DoResize(d_width, d_height); Activate(d_fore ? d_fore->w_norefresh : 0); } else Msg(0, "Your termcap does not specify how to change the terminal's height to %d.", n); break; case RC_AKA: case RC_TITLE: if (*args == 0) InputAKA(); else ChangeAKA(fore, *args, 20); break; case RC_COLON: InputColon(); break; case RC_LASTMSG: if (d_status_lastmsg) Msg(0, "%s", d_status_lastmsg); break; case RC_SCREEN: DoScreen("key", args); break; case RC_WRAP: if (ParseSwitch(act, &fore->w_wrap) == 0 && msgok) Msg(0, "%cwrap", fore->w_wrap ? '+' : '-'); break; case RC_FLOW: if (*args) { if (args[0][0] == 'a') { fore->w_flow = (fore->w_flow & FLOW_AUTO) ? FLOW_AUTOFLAG |FLOW_AUTO|FLOW_NOW : FLOW_AUTOFLAG; } else { if (ParseOnOff(act, &n)) break; fore->w_flow = (fore->w_flow & FLOW_AUTO) | n; } } else { if (fore->w_flow & FLOW_AUTOFLAG) fore->w_flow = (fore->w_flow & FLOW_AUTO) | FLOW_NOW; else if (fore->w_flow & FLOW_NOW) fore->w_flow &= ~FLOW_NOW; else fore->w_flow = fore->w_flow ? FLOW_AUTOFLAG|FLOW_AUTO|FLOW_NOW : FLOW_AUTOFLAG; } SetFlow(fore->w_flow & FLOW_NOW); if (msgok) Msg(0, "%cflow%s", (fore->w_flow & FLOW_NOW) ? '+' : '-', (fore->w_flow & FLOW_AUTOFLAG) ? "(auto)" : ""); break; case RC_WRITELOCK: if (*args) { if (args[0][0] == 'a') { fore->w_wlock = WLOCK_AUTO; } else { if (ParseOnOff(act, &n)) break; fore->w_wlock = n ? WLOCK_ON : WLOCK_OFF; } } fore->w_wlockuser = d_user; Msg(0, "writelock %s", (fore->w_wlock == WLOCK_AUTO) ? "auto" : ((fore->w_wlock == WLOCK_OFF) ? "off" : "on")); break; case RC_CLEAR: if (fore->w_state == LIT) WriteString(fore, "\033[H\033[J", 6); break; case RC_RESET: if (fore->w_state == LIT) WriteString(fore, "\033c", 2); break; case RC_MONITOR: n = fore->w_monitor == MON_ON; if (ParseSwitch(act, &n)) break; if (n) { fore->w_monitor = MON_ON; #ifdef NETHACK if (nethackflag) Msg(0, "You feel like someone is watching you..."); else #endif Msg(0, "Window %d (%s) is now being monitored for all activity.", fore->w_number, fore->w_title); } else { fore->w_monitor = MON_OFF; #ifdef NETHACK if (nethackflag) Msg(0, "You no longer sense the watcher's presence."); else #endif Msg(0, "Window %d (%s) is no longer being monitored for activity.", fore->w_number, fore->w_title); } break; case RC_DISPLAYS: display_displays(); break; case RC_HELP: display_help(); break; case RC_LICENSE: display_copyright(); break; #ifdef COPY_PASTE case RC_COPY: if (d_layfn != &WinLf) { Msg(0, "Must be on a window layer"); break; } MarkRoutine(); break; case RC_HISTORY: if (d_layfn != &WinLf) { Msg(0, "Must be on a window layer"); break; } if (GetHistory() == 0) break; if (d_user->u_copybuffer == NULL) break; /*FALLTHROUGH*/ case RC_PASTE: { char *ss; int l = 0; if ((s = *args) == 0) s = "."; for (ss = s; (ch = *ss); ss++) { if (ch == '.') l += d_user->u_copylen; else l += plop_tab[(int)(unsigned char)ch].len; } if (l == 0) { #ifdef NETHACK if (nethackflag) Msg(0, "Nothing happens."); else #endif Msg(0, "empty buffer"); break; } fore->w_pasteptr = 0; fore->w_pastelen = 0; if (fore->w_pastebuf) free(fore->w_pastebuf); fore->w_pastebuf = 0; if (s[1] == 0) { if (*s == '.') fore->w_pasteptr = d_user->u_copybuffer; else fore->w_pasteptr = plop_tab[(int)(unsigned char)ch].buf; fore->w_pastelen = l; break; } if ((fore->w_pastebuf = (char *)malloc(l)) == 0) { Msg(0, strnomem); break; } l = 0; for (ss = s; (ch = *ss); ss++) { if (ch == '.') { bcopy(d_user->u_copybuffer, fore->w_pastebuf + l, d_user->u_copylen); l += d_user->u_copylen; } else { bcopy(plop_tab[(int)(unsigned char)ch].buf, fore->w_pastebuf + l, plop_tab[(int)(unsigned char)ch].len); l += plop_tab[(int)(unsigned char)ch].len; } } fore->w_pasteptr = fore->w_pastebuf; fore->w_pastelen = l; break; } case RC_WRITEBUF: if (d_user->u_copybuffer == NULL) { #ifdef NETHACK if (nethackflag) Msg(0, "Nothing happens."); else #endif Msg(0, "empty buffer"); break; } WriteFile(DUMP_EXCHANGE); break; case RC_READBUF: ReadFile(); break; case RC_REMOVEBUF: KillBuffers(); break; #endif /* COPY_PASTE */ case RC_ESCAPE: FreeKey((int)(unsigned char)d_user->u_Esc); FreeKey((int)(unsigned char)d_user->u_MetaEsc); if (ParseEscape(d_user, *args)) { Msg(0, "%s: two characters required after escape.", rc_name); break; } FreeKey((int)(unsigned char)d_user->u_Esc); FreeKey((int)(unsigned char)d_user->u_MetaEsc); ktab[(int)(unsigned char)d_user->u_Esc].nr = RC_OTHER; ktab[(int)(unsigned char)d_user->u_MetaEsc].nr = RC_META; break; case RC_CHDIR: s = *args ? *args : home; if (chdir(s) == -1) Msg(errno, "%s", s); break; case RC_SHELL: if (ParseSaveStr(act, &ShellProg) == 0) ShellArgs[0] = ShellProg; break; case RC_HARDCOPYDIR: (void)ParseSaveStr(act, &hardcopydir); break; case RC_LOGDIR: (void)ParseSaveStr(act, &screenlogdir); break; case RC_SHELLTITLE: case RC_SHELLAKA: (void)ParseSaveStr(act, &nwin_default.aka); break; case RC_SLEEP: case RC_TERMCAP: case RC_TERMINFO: break; /* Already handled */ case RC_TERM: s = NULL; if (ParseSaveStr(act, &s)) break; if (strlen(s) >= 20) { Msg(0,"%s: term: argument too long ( < 20)", rc_name); free(s); break; } strcpy(screenterm, s); free(s); debug1("screenterm set to %s\n", screenterm); MakeTermcap(display == 0); debug("new termcap made\n"); break; case RC_ECHO: if (msgok) { /* * d_user typed ^A:echo... well, echo isn't FinishRc's job, * but as he wanted to test us, we show good will */ if (*args && (args[1] == 0 || (strcmp(args[1], "-n") == 0 && args[2] == 0))) Msg(0, "%s", args[1] ? args[1] : *args); else Msg(0, "%s: 'echo [-n] \"string\"' expected.", rc_name); } break; case RC_BELL: (void)ParseSaveStr(act, &BellString); break; #ifdef COPY_PASTE case RC_BUFFERFILE: if (*args == 0) BufferFile = SaveStr(DEFAULT_BUFFERFILE); else if (ParseSaveStr(act, &BufferFile)) break; if (msgok) Msg(0, "Bufferfile is now '%s'\n", BufferFile); break; #endif case RC_ACTIVITY: (void)ParseSaveStr(act, &ActivityString); break; #ifdef POW_DETACH case RC_POW_DETACH_MSG: (void)ParseSaveStr(act, &PowDetachString); break; #endif #if defined(UTMPOK) && defined(LOGOUTOK) case RC_DEFLOGIN: (void)ParseOnOff(act, &nwin_default.lflag); break; case RC_LOGIN: n = fore->w_slot != (slot_t)-1; if (ParseSwitch(act, &n) == 0) SlotToggle(n); break; #endif case RC_DEFFLOW: if (args[0] && args[1] && args[1][0] == 'i') { iflag = 1; if ((intrc == VDISABLE) && (origintrc != VDISABLE)) { #if defined(TERMIO) || defined(POSIX) intrc = d_NewMode.tio.c_cc[VINTR] = origintrc; d_NewMode.tio.c_lflag |= ISIG; #else /* TERMIO || POSIX */ intrc = d_NewMode.m_tchars.t_intrc = origintrc; #endif /* TERMIO || POSIX */ if (display) SetTTY(d_userfd, &d_NewMode); } } if (args[0] && args[0][0] == 'a') nwin_default.flowflag = FLOW_AUTOFLAG; else (void)ParseOnOff(act, &nwin_default.flowflag); break; case RC_DEFWRAP: (void)ParseOnOff(act, &default_wrap); break; case RC_HARDSTATUS: RemoveStatus(); (void)ParseSwitch(act, &use_hardstatus); break; case RC_DEFMONITOR: if (ParseOnOff(act, &n) == 0) default_monitor = (n == 0) ? MON_OFF : MON_ON; break; case RC_CONSOLE: n = (console_window != 0); if (ParseSwitch(act, &n)) break; if (TtyGrabConsole(fore->w_ptyfd, n, rc_name)) break; if (n == 0) Msg(0, "%s: releasing console %s", rc_name, HostName); else if (console_window) Msg(0, "%s: stealing console %s from window %d (%s)", rc_name, HostName, console_window->w_number, console_window->w_title); else Msg(0, "%s: grabbing console %s", rc_name, HostName); console_window = n ? fore : 0; break; case RC_ALLPARTIAL: if (ParseOnOff(act, &all_norefresh)) break; if (!all_norefresh && fore) Activate(-1); if (msgok) Msg(0, all_norefresh ? "No refresh on window change!\n" : "Window specific refresh\n"); break; case RC_PARTIAL: (void)ParseSwitch(act, &n); fore->w_norefresh = n; break; case RC_VBELL: if (ParseSwitch(act, &visual_bell) || !msgok) break; if (visual_bell == 0) { #ifdef NETHACK if (nethackflag) Msg(0, "Suddenly you can't see your bell!"); else #endif Msg(0, "switched to audible bell."); } else { #ifdef NETHACK if (nethackflag) Msg(0, "Your bell is no longer invisible."); else #endif Msg(0, "switched to visual bell."); } break; case RC_VBELLWAIT: if (ParseNum(act, &VBellWait) == 0 && msgok) Msg(0, "vbellwait set to %d seconds", VBellWait); break; case RC_MSGWAIT: if (ParseNum(act, &MsgWait) == 0 && msgok) Msg(0, "msgwait set to %d seconds", MsgWait); break; case RC_MSGMINWAIT: if (ParseNum(act, &MsgMinWait) == 0 && msgok) Msg(0, "msgminwait set to %d seconds", MsgMinWait); break; case RC_SILENCEWAIT: if ((ParseNum(act, &SilenceWait) == 0) && msgok) { if (SilenceWait < 1) SilenceWait = 1; for (p = windows; p; p = p->w_next) if (p->w_tstamp.seconds) p->w_tstamp.seconds = SilenceWait; Msg(0, "silencewait set to %d seconds", SilenceWait); } break; case RC_NUMBER: if (*args == 0) Msg(0, "This is window %d (%s).\n", fore->w_number, fore->w_title); else { int old = fore->w_number; if (ParseNum(act, &n) || n >= MAXWIN) break; p = wtab[n]; wtab[n] = fore; fore->w_number = n; wtab[old] = p; if (p) p->w_number = old; #ifdef MULTIUSER AclWinSwap(old, n); #endif } break; case RC_SILENCE: n = fore->w_tstamp.seconds != 0; i = SilenceWait; if (args[0] && (args[0][0] == '-' || (args[0][0] >= '0' && args[0][0] <= '9'))) { if (ParseNum(act, &i)) break; n = i; } else if (ParseSwitch(act, &n)) break; if (n) { fore->w_tstamp.lastio = time(0); fore->w_tstamp.seconds = i; if (!msgok) break; #ifdef NETHACK if (nethackflag) Msg(0, "You feel like someone is waiting for %d sec. silence...", fore->w_tstamp.seconds); else #endif Msg(0, "Window %d (%s) is now being monitored for %d sec. silence.", fore->w_number, fore->w_title, fore->w_tstamp.seconds); } else { fore->w_tstamp.lastio = (time_t)0; fore->w_tstamp.seconds = 0; if (!msgok) break; #ifdef NETHACK if (nethackflag) Msg(0, "You no longer sense the watcher's silence."); else #endif Msg(0, "Window %d (%s) is no longer being monitored for silence.", fore->w_number, fore->w_title); } break; #ifdef COPY_PASTE case RC_DEFSCROLLBACK: (void)ParseNum(act, &nwin_default.histheight); break; case RC_SCROLLBACK: (void)ParseNum(act, &n); ChangeScrollback(fore, n, d_width); if (msgok) Msg(0, "scrollback set to %d", fore->w_histheight); break; #endif case RC_SESSIONNAME: if (*args == 0) Msg(0, "This session is named '%s'\n", SockNamePtr); else { char buf[MAXPATHLEN]; s = NULL; if (ParseSaveStr(act, &s)) break; if (!*s || strlen(s) > MAXPATHLEN - 13) { Msg(0, "%s: bad session name '%s'\n", rc_name, s); free(s); break; } sprintf(buf, "%s", SockPath); sprintf(buf + (SockNamePtr - SockPath), "%d.%s", getpid(), s); free(s); if ((access(buf, F_OK) == 0) || (errno != ENOENT)) { Msg(0, "%s: inappropriate path: '%s'.", rc_name, buf); break; } if (rename(SockPath, buf)) { Msg(errno, "%s: failed to rename(%s, %s)", rc_name, SockPath, buf); break; } debug2("rename(%s, %s) done\n", SockPath, buf); sprintf(SockPath, "%s", buf); MakeNewEnv(); } break; case RC_SETENV: if (!args[0] || !args[1]) { debug1("RC_SETENV arguments missing: %s\n", args[0] ? args[0] : ""); InputSetenv(args[0]); } else #ifndef USESETENV { char *buf; int l; if ((buf = (char *)malloc((l = strlen(args[0])) + strlen(args[1]) + 2)) == NULL) { Msg(0, strnomem); break; } strcpy(buf, args[0]); buf[l] = '='; strcpy(buf + l + 1, args[1]); putenv(buf); # ifdef NEEDPUTENV /* * we use our own putenv(), knowing that it does a malloc() * the string space, we can free our buf now. */ free(buf); # else /* NEEDSETENV */ /* * For all sysv-ish systems that link a standard putenv() * the string-space buf is added to the environment and must not * be freed, or modified. * We are sorry to say that memory is lost here, when setting * the same variable again and again. */ # endif /* NEEDSETENV */ } #else /* USESETENV */ # if defined(linux) || defined(__386BSD__) || defined(BSDI) setenv(args[0], args[1], 0); # else setenv(args[0], args[1]); # endif /* linux || __386BSD__ || BSDI */ #endif /* USESETENV */ MakeNewEnv(); break; case RC_UNSETENV: unsetenv(*args); MakeNewEnv(); break; case RC_SLOWPASTE: if (ParseNum(act, &slowpaste) == 0 && msgok) Msg(0, "slowpaste set to %d milliseconds", slowpaste); break; #ifdef COPY_PASTE case RC_MARKKEYS: s = NULL; if (ParseSaveStr(act, &s)) break; if (CompileKeys(s, mark_key_tab)) { Msg(0, "%s: markkeys: syntax error.", rc_name); free(s); break; } debug1("markkeys %s\n", *args); free(s); break; #endif #ifdef NETHACK case RC_NETHACK: (void)ParseOnOff(act, &nethackflag); break; #endif case RC_HARDCOPY_APPEND: (void)ParseOnOff(act, &hardcopy_append); break; case RC_VBELL_MSG: (void)ParseSaveStr(act, &VisualBellString); debug1(" new vbellstr '%s'\n", VisualBellString); break; case RC_DEFMODE: if (ParseOct(act, &n)) break; if (n < 0 || n > 0777) { Msg(0, "%s: mode: Invalid tty mode %o", rc_name, n); break; } TtyMode = n; if (msgok) Msg(0, "Ttymode set to %03o", TtyMode); break; #ifdef COPY_PASTE case RC_CRLF: (void)ParseOnOff(act, &join_with_cr); break; #endif case RC_AUTODETACH: (void)ParseOnOff(act, &auto_detach); break; case RC_STARTUP_MESSAGE: (void)ParseOnOff(act, &default_startup); break; #ifdef PASSWORD case RC_PASSWORD: CheckPassword = 1; if (*args) { strncpy(Password, *args, sizeof(Password) - 1); if (!strcmp(Password, "none")) CheckPassword = 0; } else { if (display == 0) { debug("prompting for password on no display???\n"); break; } Input("New screen password:", sizeof(Password) - 1, pass1, INP_NOECHO); } break; #endif /* PASSWORD */ case RC_BIND: if ((s = ParseChar(*args, &ch)) == NULL || *s) { Msg(0, "%s: bind: character, ^x, or (octal) \\032 expected.", rc_name); break; } n = (unsigned char)ch; FreeKey(n); if (args[1]) { if ((i = FindCommnr(args[1])) == RC_ILLEGAL) { Msg(0, "%s: bind: unknown command '%s'", rc_name, args[1]); break; } if (CheckArgNum(i, args + 2)) break; ktab[n].nr = i; if (args[2]) ktab[n].args = SaveArgs(args + 2); } break; #ifdef MULTIUSER case RC_ACLCHG: case RC_ACLADD: { struct user **u; u = FindUserPtr(args[0]); UserAdd(args[0], NULL, u); if (args[1] && args[2]) AclSetPerm(*u, args[1], args[2]); else AclSetPerm(*u, "+rwx", "#?"); break; } case RC_ACLDEL: { if (UserDel(args[0], NULL)) break; if (msgok) Msg(0, "%s removed from acl database", args[0]); break; } case RC_ACLGRP: { break; } case RC_MULTIUSER: if (ParseOnOff(act, &n)) break; multi = n ? "" : 0; chsock(); if (msgok) Msg(0, "Multiuser mode %s", multi ? "enabled" : "disabled"); break; #endif /* MULTIUSER */ #ifdef PSEUDOS case RC_EXEC: winexec(args); break; #endif #ifdef MULTI case RC_CLONE: execclone(args); break; #endif default: break; } } void DoCommand(argv) char **argv; { struct action act; if ((act.nr = FindCommnr(*argv)) == RC_ILLEGAL) { Msg(0, "%s: unknown command '%s'", rc_name, *argv); return; } act.args = argv + 1; DoAction(&act, -1); } static char ** SaveArgs(args) char **args; { register char **ap, **pp; register int argc = 0; while (args[argc]) argc++; if ((pp = ap = (char **) malloc((unsigned) (argc + 1) * sizeof(char **))) == 0) Panic(0, strnomem); while (argc--) { *pp++ = SaveStr(*args++); } *pp = 0; return ap; } int Parse(buf, args) char *buf, **args; { register char *p = buf, **ap = args; register int delim, argc; argc = 0; for (;;) { while (*p && (*p == ' ' || *p == '\t')) ++p; if (argc == 0) { /* * Expand hardcoded shortcuts. * This should not be done here, cause multiple commands per * line or prefixed commands won't be recognized. * But as spaces between shortcut character and arguments * can be ommited this expansion affects tokenisation and * should be done here. Hmmm. jw. */ switch (*p) { case '@': *ap++ = "at"; while (*(++p) == ' ' || *p == '\t') ; argc++; break; #ifdef PSEUDOS case '!': case '|': *ap++ = "exec"; if (*p == '!') p++; while (*p == ' ' || *p == '\t') p++; argc++; break; #endif } } if (*p == '\0' || *p == '#') { *p = '\0'; args[argc] = 0; return argc; } if (++argc >= MAXARGS) { Msg(0, "%s: too many tokens.", rc_name); return 0; } delim = 0; if (*p == '"' || *p == '\'') delim = *p++; *ap++ = p; while (*p && !(delim ? *p == delim : (*p == ' ' || *p == '\t'))) ++p; if (*p == '\0') { if (delim) { Msg(0, "%s: Missing quote.", rc_name); return 0; } } else *p++ = '\0'; } } int ParseEscape(u, p) struct user *u; char *p; { if ((p = ParseChar(p, u ? &u->u_Esc : &DefaultEsc)) == NULL || (p = ParseChar(p, u ? &u->u_MetaEsc : &DefaultMetaEsc)) == NULL || *p) return -1; return 0; } static int ParseSwitch(act, var) struct action *act; int *var; { if (*act->args == 0) { *var ^= 1; return 0; } return ParseOnOff(act, var); } static int ParseOnOff(act, var) struct action *act; int *var; { register int num = -1; char **args = act->args; if (args[1] == 0) { if (strcmp(args[0], "on") == 0) num = 1; else if (strcmp(args[0], "off") == 0) num = 0; } if (num < 0) { Msg(0, "%s: %s: invalid argument. Give 'on' or 'off'", rc_name, comms[act->nr].name); return -1; } *var = num; return 0; } static int ParseSaveStr(act, var) struct action *act; char **var; { char **args = act->args; if (*args == 0 || args[1]) { Msg(0, "%s: %s: one argument required.", rc_name, comms[act->nr].name); return -1; } if (*var) free(*var); *var = SaveStr(*args); return 0; } static int ParseNum(act, var) struct action *act; int *var; { int i; char *p, **args = act->args; p = *args; if (p == 0 || *p == 0 || args[1]) { Msg(0, "%s: %s: invalid argument. Give one argument.", rc_name, comms[act->nr].name); return -1; } i = 0; while (*p) { if (*p >= '0' && *p <= '9') i = 10 * i + (*p - '0'); else { Msg(0, "%s: %s: invalid argument. Give numeric argument.", rc_name, comms[act->nr].name); return -1; } p++; } debug1("ParseNum got %d\n", i); *var = i; return 0; } static struct win * WindowByName(s) char *s; { struct win *p; for (p = windows; p; p = p->w_next) if (!strncmp(p->w_title, s, strlen(s))) return p; return NULL; } static int WindowByNumber(str) char *str; { int i; char *s; for (i = 0, s = str; *s; s++) { if (*s < '0' || *s > '9') break; i = i * 10 + (*s - '0'); } return *s ? -1 : i; } /* * Get window number from Name or Number string. * Numbers are tried first, then names, a prefix match suffices. * Be careful when assigning numeric strings as WindowTitles. */ int WindowByNoN(str) char *str; { int i; struct win *p; if ((i = WindowByNumber(str)) < 0 || i >= MAXWIN) { if ((p = WindowByName(str))) return p->w_number; return -1; } return i; } static int ParseWinNum(act, var) struct action *act; int *var; { char **args = act->args; int i = 0; if (*args == 0 || args[1]) { Msg(0, "%s: %s: one argument required.", rc_name, comms[act->nr].name); return -1; } i = WindowByNoN(*args); if (i < 0) { Msg(0, "%s: %s: invalid argument. Give window number or name.", rc_name, comms[act->nr].name); return -1; } debug1("ParseWinNum got %d\n", i); *var = i; return 0; } static int ParseOct(act, var) struct action *act; int *var; { char *p, **args = act->args; int i = 0; p = *args; if (p == 0 || *p == 0 || args[1]) { Msg(0, "%s: %s: invalid argument. Give one octal argument.", rc_name, comms[act->nr].name); return -1; } while (*p) { if (*p >= '0' && *p <= '7') i = 8 * i + (*p - '0'); else { Msg(0, "%s: %s: invalid argument. Give octal argument.", rc_name, comms[act->nr].name); return -1; } p++; } debug1("ParseOct got %d\n", i); *var = i; return 0; } static char * ParseChar(p, cp) char *p, *cp; { if (*p == 0) return 0; if (*p == '^') { if (*++p == '?') *cp = '\177'; else if (*p >= '@') *cp = Ctrl(*p); else return 0; ++p; } else if (*p == '\\' && *++p <= '7' && *p >= '0') { *cp = 0; do *cp = *cp * 8 + *p - '0'; while (*++p <= '7' && *p >= '0'); } else *cp = *p++; return p; } static int IsNum(s, base) register char *s; register int base; { for (base += '0'; *s; ++s) if (*s < '0' || *s > base) return 0; return 1; } static int IsNumColon(s, base, p, psize) int base, psize; char *s, *p; { char *q; if ((q = rindex(s, ':')) != NULL) { strncpy(p, q + 1, psize - 1); p[psize - 1] = '\0'; *q = '\0'; } else *p = '\0'; return IsNum(s, base); } static void SwitchWindow(n) int n; { struct win *p; debug1("SwitchWindow %d\n", n); if (display == 0) return; if (n < 0 || n >= MAXWIN || (p = wtab[n]) == 0) { ShowWindows(); return; } if (p == d_fore) { Msg(0, "This IS window %d (%s).", n, p->w_title); return; } if (p->w_display) { Msg(0, "Window %d (%s) is on another display (%s@%s).", n, p->w_title, p->w_display->_d_user->u_name, p->w_display->_d_usertty); return; } SetForeWindow(p); Activate(fore->w_norefresh); } /* * returns 0, if the lock really has been released */ int ReleaseAutoWritelock(dis, w) struct display *dis; struct win *w; { /* release auto writelock when user has no other display here */ if (w->w_wlock == WLOCK_AUTO && w->w_wlockuser == d_user) { struct display *d; for (d = displays; d; d = d->_d_next) if (( d != display) && (d->_d_fore == w)) break; debug3("%s %s autolock on win %d\n", d_user->u_name, d?"keeps":"releases", w->w_number); if (!d) { w->w_wlockuser = NULL; return 0; } } return 1; } void SetForeWindow(wi) struct win *wi; { struct win *p, **pp; struct layer *l; /* * If we come from another window, make it inactive. */ if (display) { fore = d_fore; if (fore) { ReleaseAutoWritelock(display, fore); /* deactivate old window. */ if (fore->w_tstamp.seconds) fore->w_tstamp.lastio = Now; d_other = fore; fore->w_active = 0; fore->w_display = 0; } else { /* put all the display layers on the window. */ for (l = d_lay; l; l = l->l_next) if (l->l_next == &BlankLayer) { l->l_next = wi->w_lay; wi->w_lay = d_lay; for (l = d_lay; l != wi->w_lay; l = l->l_next) l->l_block |= wi->w_lay->l_block; break; } } d_fore = wi; if (d_other == wi) d_other = 0; d_lay = wi->w_lay; d_layfn = d_lay->l_layfn; if ((wi->w_wlock == WLOCK_AUTO) && !wi->w_wlockuser) { debug2("%s obtained auto writelock for window %d\n", d_user->u_name, wi->w_number); wi->w_wlockuser = d_user; } } fore = wi; fore->w_display = display; if (!fore->w_lay) fore->w_active = 1; /* * Place the window at the head of the most-recently-used list. */ for (pp = &windows; (p = *pp); pp = &p->w_next) if (p == wi) break; ASSERT(p); *pp = p->w_next; p->w_next = windows; windows = p; } static int NextWindow() { register struct win **pp; int n = fore ? fore->w_number : 0; for (pp = wtab + n + 1; pp != wtab + n; pp++) { if (pp == wtab + MAXWIN) pp = wtab; if (*pp) break; } return pp - wtab; } static int PreviousWindow() { register struct win **pp; int n = fore ? fore->w_number : MAXWIN - 1; for (pp = wtab + n - 1; pp != wtab + n; pp--) { if (pp < wtab) pp = wtab + MAXWIN - 1; if (*pp) break; } return pp - wtab; } static int MoreWindows() { if (windows && windows->w_next) return 1; if (fore == 0) { Msg(0, "No window available"); return 0; } #ifdef NETHACK if (nethackflag) Msg(0, "You cannot escape from window %d!", fore->w_number); else #endif Msg(0, "No other window."); return 0; } void KillWindow(wi) struct win *wi; { struct win **pp, *p; display = wi->w_display; if (display) { if (wi == d_fore) { RemoveStatus(); if (d_lay != &wi->w_winlay) ExitOverlayPage(); d_fore = 0; d_lay = &BlankLayer; d_layfn = BlankLayer.l_layfn; } } for (pp = &windows; (p = *pp); pp = &p->w_next) if (p == wi) break; ASSERT(p); *pp = p->w_next; /* * Remove window from linked list. */ wi->w_inlen = 0; wtab[wi->w_number] = 0; FreeWindow(wi); /* * If the foreground window disappeared check the head of the linked list * of windows for the most recently used window. If no window is alive at * all, exit. */ if (display && d_fore) return; if (windows == 0) Finit(0); SwitchWindow(windows->w_number); } static void LogToggle(on) int on; { char buf[1024]; if ((fore->w_logfp != 0) == on) { if (display && !*rc_name) Msg(0, "You are %s logging.", on ? "already" : "not"); return; } if (screenlogdir) sprintf(buf, "%s/screenlog.%d", screenlogdir, fore->w_number); else sprintf(buf, "screenlog.%d", fore->w_number); if (fore->w_logfp != NULL) { #ifdef NETHACK if (nethackflag) Msg(0, "You put away your scroll of logging named \"%s\".", buf); else #endif Msg(0, "Logfile \"%s\" closed.", buf); fclose(fore->w_logfp); fore->w_logfp = NULL; return; } if ((fore->w_logfp = secfopen(buf, "a")) == NULL) { #ifdef NETHACK if (nethackflag) Msg(0, "You don't seem to have a scroll of logging named \"%s\".", buf); else #endif Msg(errno, "Error opening logfile \"%s\"", buf); return; } #ifdef NETHACK if (nethackflag) Msg(0, "You %s your scroll of logging named \"%s\".", ftell(fore->w_logfp) ? "add to" : "start writing on", buf); else #endif Msg(0, "%s logfile \"%s\"", ftell(fore->w_logfp) ? "Appending to" : "Creating", buf); } void ShowWindows() { char buf[1024]; register char *s, *ss; register struct win **pp, *p; register char *cmd; ASSERT(display); s = ss = buf; for (pp = wtab; pp < wtab + MAXWIN; pp++) { if ((p = *pp) == 0) continue; cmd = p->w_title; if (s - buf + strlen(cmd) > sizeof(buf) - 6) break; if (s > buf) { *s++ = ' '; *s++ = ' '; } sprintf(s, "%d", p->w_number); s += strlen(s); if (p == fore) { ss = s; *s++ = '*'; } else if (p == d_other) *s++ = '-'; if (p->w_display && p->w_display != display) *s++ = '&'; if (p->w_monitor == MON_DONE || p->w_monitor == MON_MSG) *s++ = '@'; if (p->w_bell == BELL_DONE || p->w_bell == BELL_MSG) *s++ = '!'; #ifdef UTMPOK if (p->w_slot != (slot_t) 0 && p->w_slot != (slot_t) -1) *s++ = '$'; #endif if (p->w_logfp != NULL) { strcpy(s, "(L)"); s += 3; } if (p->w_ptyfd < 0) *s++ = 'Z'; *s++ = ' '; strcpy(s, cmd); s += strlen(s); if (p == fore) { /* * this is usually done by Activate(), but when looking * on your current window, you may get annoyed, as there is still * that temporal '!' and '@' displayed. * So we remove that after displaying it once. */ p->w_bell = BELL_OFF; if (p->w_monitor != MON_OFF) p->w_monitor = MON_ON; } } *s++ = ' '; *s = '\0'; if (ss - buf > d_width / 2) { ss -= d_width / 2; if (s - ss < d_width) { ss = s - d_width; if (ss < buf) ss = buf; } } else ss = buf; Msg(0, "%s", ss); } static void ShowTime() { char buf[512]; struct tm *tp; time_t now; (void) time(&now); tp = localtime(&now); sprintf(buf, "%2d:%02d:%02d %s", tp->tm_hour, tp->tm_min, tp->tm_sec, HostName); #ifdef LOADAV AddLoadav(buf + strlen(buf)); #endif /* LOADAV */ Msg(0, "%s", buf); } static void ShowInfo() { char buf[512], *p; register struct win *wp = fore; register int i; if (wp == 0) { Msg(0, "(%d,%d)/(%d,%d) no window", d_x + 1, d_y + 1, d_width, d_height); return; } #ifdef COPY_PASTE sprintf(buf, "(%d,%d)/(%d,%d)+%d %c%sflow %cins %corg %cwrap %capp %clog %cmon %cr", #else sprintf(buf, "(%d,%d)/(%d,%d) %c%sflow %cins %corg %cwrap %capp %clog %cmon %cr", #endif wp->w_x + 1, wp->w_y + 1, wp->w_width, wp->w_height, #ifdef COPY_PASTE wp->w_histheight, #endif (wp->w_flow & FLOW_NOW) ? '+' : '-', (wp->w_flow & FLOW_AUTOFLAG) ? "" : ((wp->w_flow & FLOW_AUTO) ? "(+)" : "(-)"), wp->w_insert ? '+' : '-', wp->w_origin ? '+' : '-', wp->w_wrap ? '+' : '-', wp->w_keypad ? '+' : '-', (wp->w_logfp != NULL) ? '+' : '-', (wp->w_monitor != MON_OFF) ? '+' : '-', wp->w_norefresh ? '-' : '+'); if (CG0) { p = buf + strlen(buf); sprintf(p, " G%1d [", wp->w_Charset); for (i = 0; i < 4; i++) p[i + 5] = wp->w_charsets[i] ? wp->w_charsets[i] : 'B'; p[9] = ']'; p[10] = '\0'; } Msg(0, "%s", buf); } static void AKAfin(buf, len) char *buf; int len; { ASSERT(display); if (len && fore) ChangeAKA(fore, buf, 20); } static void InputAKA() { Input("Set window's title to: ", 20, AKAfin, INP_COOKED); } static void Colonfin(buf, len) char *buf; int len; { if (len) RcLine(buf); } static void InputColon() { Input(":", 100, Colonfin, INP_COOKED); } static void SelectFin(buf, len) char *buf; int len; { int n; if (!len || !display) return; if ((n = WindowByNoN(buf)) < 0) return; SwitchWindow(n); } static void InputSelect() { Input("Switch to window: ", 20, SelectFin, INP_COOKED); } static char setenv_var[31]; static void SetenvFin1(buf, len) char *buf; int len; { if (!len || !display) return; InputSetenv(buf); } static void SetenvFin2(buf, len) char *buf; int len; { struct action act; char *args[3]; if (!len || !display) return; act.nr = RC_SETENV; args[0] = setenv_var; args[1] = buf; args[2] = NULL; act.args = args; debug2("SetenvFin2: setenv '%s' '%s'\n", setenv_var, buf); DoAction(&act, -1); } static void InputSetenv(arg) char *arg; { static char setenv_buf[80]; /* need to be static here, cannot be freed */ if (arg) { strncpy(setenv_var, arg, 30); sprintf(setenv_buf, "Enter value for %s: ", arg); Input(setenv_buf, 30, SetenvFin2, INP_COOKED); } else Input("Setenv: Enter variable name: ", 30, SetenvFin1, INP_COOKED); } void DoScreen(fn, av) char *fn, **av; { struct NewWindow nwin; register int num; char buf[20]; char termbuf[25]; nwin = nwin_undef; termbuf[0] = '\0'; while (av && *av && av[0][0] == '-') { switch (av[0][1]) { case 'f': switch (av[0][2]) { case 'n': case '0': nwin.flowflag = FLOW_NOW * 0; break; case 'y': case '1': case '\0': nwin.flowflag = FLOW_NOW * 1; break; case 'a': nwin.flowflag = FLOW_AUTOFLAG; break; default: break; } break; case 'k': case 't': if (av[0][2]) nwin.aka = &av[0][2]; else if (*++av) nwin.aka = *av; else --av; break; case 'T': if (av[0][2]) nwin.term = &av[0][2]; else if (*++av) nwin.term = *av; else --av; break; case 'h': if (av[0][2]) nwin.histheight = atoi(av[0] + 2); else if (*++av) nwin.histheight = atoi(*av); else --av; break; #ifdef LOGOUTOK case 'l': switch (av[0][2]) { case 'n': case '0': nwin.lflag = 0; break; case 'y': case '1': case '\0': nwin.lflag = 1; break; default: break; } break; #endif case 'a': nwin.aflag = 1; break; case 'M': nwin.monitor = MON_ON; debug("nwin.monitor = MON_ON;\n"); break; default: Msg(0, "%s: screen: invalid option -%c.", fn, av[0][1]); break; } ++av; } num = 0; if (av && *av && IsNumColon(*av, 10, buf, sizeof(buf))) { if (*buf != '\0') nwin.aka = buf; num = atoi(*av); if (num < 0 || num > MAXWIN - 1) { Msg(0, "%s: illegal screen number %d.", fn, num); num = 0; } nwin.StartAt = num; ++av; } if (av && *av) { nwin.args = av; if (!nwin.aka) nwin.aka = Filename(*av); } MakeWindow(&nwin); } #ifdef COPY_PASTE /* * CompileKeys must be called before Markroutine is first used. * to initialise the keys with defaults, call CompileKeys(NULL, mark_key_tab); * * s is an ascii string in a termcap-like syntax. It looks like * "j=u:k=d:l=r:h=l: =.:" and so on... * this example rebinds the cursormovement to the keys u (up), d (down), * l (left), r (right). placing a mark will now be done with ".". */ int CompileKeys(s, array) char *s, *array; { int i; unsigned char key, value; if (!s || !*s) { for (i = 0; i < 256; i++) array[i] = i; return 0; } debug1("CompileKeys: '%s'\n", s); while (*s) { s = ParseChar(s, (char *) &key); if (*s != '=') return -1; do { s = ParseChar(++s, (char *) &value); array[value] = key; } while (*s == '='); if (!*s) break; if (*s++ != ':') return -1; } return 0; } #endif /* COPY_PASTE */ /* * Asynchronous input functions */ #ifdef POW_DETACH static void pow_detach_fn(buf, len) char *buf; int len; { if (len) { *buf = 0; return; } if (ktab[(int)(unsigned char)*buf].nr != RC_POW_DETACH) { if (display) write(d_userfd, "\007", 1); #ifdef NETHACK if (nethackflag) Msg(0, "The blast of disintegration whizzes by you!"); #endif } else Detach(D_POWER); } #endif /* POW_DETACH */ #ifdef COPY_PASTE static void copy_reg_fn(buf, len) char *buf; int len; { struct plop *pp = plop_tab + (int)(unsigned char)*buf; if (len) { *buf = 0; return; } if (pp->buf) free(pp->buf); if ((pp->buf = (char *)malloc(d_user->u_copylen)) == NULL) { Msg(0, strnomem); return; } bcopy(d_user->u_copybuffer, pp->buf, d_user->u_copylen); pp->len = d_user->u_copylen; Msg(0, "Copied %d characters into register %c", d_user->u_copylen, *buf); } static void ins_reg_fn(buf, len) char *buf; int len; { struct plop *pp = plop_tab + (int)(unsigned char)*buf; if (len) { *buf = 0; return; } if (pp->buf) { if (fore->w_pastebuf) free(fore->w_pastebuf); fore->w_pastebuf = 0; fore->w_pasteptr = pp->buf; fore->w_pastelen = pp->len; return; } #ifdef NETHACK if (nethackflag) Msg(0, "Nothing happens."); else #endif Msg(0, "Empty register."); } #endif /* COPY_PASTE */ static void process_fn(buf, len) char *buf; int len; { struct plop *pp = plop_tab + (int)(unsigned char)*buf; if (len) { *buf = 0; return; } if (pp->buf) { ProcessInput(pp->buf, pp->len); return; } #ifdef NETHACK if (nethackflag) Msg(0, "Nothing happens."); else #endif Msg(0, "Empty register."); } #ifdef PASSWORD /* ARGSUSED */ static void pass1(buf, len) char *buf; int len; { strncpy(Password, buf, sizeof(Password) - 1); Input("Retype new password:", sizeof(Password) - 1, pass2, 1); } /* ARGSUSED */ static void pass2(buf, len) char *buf; int len; { int st; char salt[2]; if (buf == 0 || strcmp(Password, buf)) { #ifdef NETHACK if (nethackflag) Msg(0, "[ Passwords don't match - your armor crumbles away ]"); else #endif /* NETHACK */ Msg(0, "[ Passwords don't match - checking turned off ]"); CheckPassword = 0; } if (Password[0] == '\0') { Msg(0, "[ No password - no secure ]"); CheckPassword = 0; } for (st = 0; st < 2; st++) salt[st] = 'A' + (int)((time(0) >> 6 * st) % 26); strncpy(Password, crypt(Password, salt), sizeof(Password)); if (CheckPassword) { #ifdef COPY_PASTE if (d_user->u_copybuffer) UserFreeCopyBuffer(d_user); d_user->u_copylen = strlen(Password); if ((d_user->u_copybuffer = (char *) malloc(d_user->u_copylen + 1)) == NULL) { Msg(0, strnomem); d_user->u_copylen = 0; } else { strcpy(d_user->u_copybuffer, Password); Msg(0, "[ Password moved into copybuffer ]"); } #else /* COPY_PASTE */ Msg(0, "[ Crypted password is \"%s\" ]", Password); #endif /* COPY_PASTE */ } if (buf) bzero(buf, strlen(buf)); } #endif /* PASSWORD */