1/* Copyright (c) 1993-2002
2 *      Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3 *      Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
4 * Copyright (c) 1987 Oliver Laumann
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program (see the file COPYING); if not, write to the
18 * Free Software Foundation, Inc.,
19 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
20 *
21 ****************************************************************
22 */
23
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <signal.h>
27#include <fcntl.h>
28#if !defined(sun) && !defined(B43) && !defined(ISC) && !defined(pyr) && !defined(_CX_UX)
29# include <time.h>
30#endif
31#include <sys/time.h>
32#ifndef sun
33#include <sys/ioctl.h>
34#endif
35
36
37#include "config.h"
38
39/* for solaris 2.1, Unixware (SVR4.2) and possibly others: */
40#ifdef SVR4
41# include <sys/stropts.h>
42#endif
43
44#include "screen.h"
45#include "extern.h"
46#include "logfile.h"
47
48extern struct comm comms[];
49extern char *rc_name;
50extern char *RcFileName, *home;
51extern char *BellString, *ActivityString, *ShellProg, *ShellArgs[];
52extern char *hstatusstring, *captionstring, *timestring;
53extern char *wliststr, *wlisttit;
54extern int captionalways;
55extern char *hardcopydir, *screenlogfile, *logtstamp_string;
56extern int log_flush, logtstamp_on, logtstamp_after;
57extern char *VisualBellString;
58extern int VBellWait, MsgWait, MsgMinWait, SilenceWait;
59extern char SockPath[], *SockName;
60extern int TtyMode, auto_detach, use_altscreen;
61extern int iflag, maxwin;
62extern int use_hardstatus, visual_bell;
63#ifdef COLOR
64extern int attr2color[][4];
65extern int nattr2color;
66#endif
67extern int hardstatusemu;
68extern char *printcmd;
69extern int default_startup;
70extern int defobuflimit;
71extern int defnonblock;
72extern int ZombieKey_destroy;
73extern int ZombieKey_resurrect;
74#ifdef AUTO_NUKE
75extern int defautonuke;
76#endif
77extern int separate_sids;
78extern struct NewWindow nwin_default, nwin_undef;
79#ifdef COPY_PASTE
80extern int join_with_cr;
81extern int compacthist;
82extern int search_ic;
83# ifdef FONT
84extern int pastefont;
85# endif
86extern unsigned char mark_key_tab[];
87extern char *BufferFile;
88#endif
89#ifdef POW_DETACH
90extern char *BufferFile, *PowDetachString;
91#endif
92#ifdef MULTIUSER
93extern struct acluser *EffectiveAclUser;	/* acl.c */
94#endif
95extern struct term term[];      /* terminal capabilities */
96#ifdef MAPKEYS
97extern char *kmapdef[];
98extern char *kmapadef[];
99extern char *kmapmdef[];
100#endif
101extern struct mchar mchar_so, mchar_null;
102extern int VerboseCreate;
103#ifdef UTF8
104extern char *screenencodings;
105#endif
106
107static int  CheckArgNum __P((int, char **));
108static void ClearAction __P((struct action *));
109static void SaveAction __P((struct action *, int, char **, int *));
110static int  NextWindow __P((void));
111static int  PreviousWindow __P((void));
112static int  MoreWindows __P((void));
113static void LogToggle __P((int));
114static void ShowInfo __P((void));
115static void ShowDInfo __P((void));
116static struct win *WindowByName __P((char *));
117static int  WindowByNumber __P((char *));
118static int  ParseOnOff __P((struct action *, int *));
119static int  ParseWinNum __P((struct action *, int *));
120static int  ParseBase __P((struct action *, char *, int *, int, char *));
121static int  ParseNum1000 __P((struct action *, int *));
122static char **SaveArgs __P((char **));
123static int  IsNum __P((char *, int));
124static void Colonfin __P((char *, int, char *));
125static void InputSelect __P((void));
126static void InputSetenv __P((char *));
127static void InputAKA __P((void));
128#ifdef MULTIUSER
129static int  InputSu __P((struct win *, struct acluser **, char *));
130static void su_fin __P((char *, int, char *));
131#endif
132static void AKAfin __P((char *, int, char *));
133#ifdef COPY_PASTE
134static void copy_reg_fn __P((char *, int, char *));
135static void ins_reg_fn __P((char *, int, char *));
136#endif
137static void process_fn __P((char *, int, char *));
138#ifdef PASSWORD
139static void pass1 __P((char *, int, char *));
140static void pass2 __P((char *, int, char *));
141#endif
142#ifdef POW_DETACH
143static void pow_detach_fn __P((char *, int, char *));
144#endif
145static void digraph_fn __P((char *, int, char *));
146static void confirm_fn __P((char *, int, char *));
147static int  IsOnDisplay __P((struct win *));
148static void ResizeRegions __P((char*));
149static void ResizeFin __P((char *, int, char *));
150static struct action *FindKtab __P((char *, int));
151
152
153extern struct layer *flayer;
154extern struct display *display, *displays;
155extern struct win *fore, *console_window, *windows;
156extern struct acluser *users;
157
158extern char screenterm[], HostName[], version[];
159extern struct NewWindow nwin_undef, nwin_default;
160extern struct LayFuncs WinLf;
161
162extern int Z0width, Z1width;
163extern int real_uid, real_gid;
164
165#ifdef NETHACK
166extern int nethackflag;
167#endif
168
169
170struct win *wtab[MAXWIN];	/* window table, should be dynamic */
171
172#ifdef MULTIUSER
173extern char *multi;
174extern int maxusercount;
175#endif
176char NullStr[] = "";
177
178struct plop plop_tab[MAX_PLOP_DEFS];
179
180#ifndef PTYMODE
181# define PTYMODE 0622
182#endif
183
184int TtyMode = PTYMODE;
185int hardcopy_append = 0;
186int all_norefresh = 0;
187#ifdef ZMODEM
188int zmodem_mode = 0;
189char *zmodem_sendcmd;
190char *zmodem_recvcmd;
191static char *zmodes[4] = {"off", "auto", "catch", "pass"};
192#endif
193
194int idletimo;
195struct action idleaction;
196#ifdef BLANKER_PRG
197char **blankerprg;
198#endif
199
200struct action ktab[256];	/* command key translation table */
201struct kclass {
202  struct kclass *next;
203  char *name;
204  struct action ktab[256];
205};
206struct kclass *kclasses;
207
208#ifdef MAPKEYS
209struct action umtab[KMAP_KEYS+KMAP_AKEYS];
210struct action dmtab[KMAP_KEYS+KMAP_AKEYS];
211struct action mmtab[KMAP_KEYS+KMAP_AKEYS];
212struct kmap_ext *kmap_exts;
213int kmap_extn;
214static int maptimeout = 300;
215#endif
216
217
218/* digraph table taken from old vim and rfc1345 */
219static const unsigned char digraphs[][3] = {
220    {' ', ' ', 160},	/* � */
221    {'N', 'S', 160},	/* � */
222    {'~', '!', 161},	/* � */
223    {'!', '!', 161},	/* � */
224    {'!', 'I', 161},	/* � */
225    {'c', '|', 162},	/* � */
226    {'c', 't', 162},	/* � */
227    {'$', '$', 163},	/* � */
228    {'P', 'd', 163},	/* � */
229    {'o', 'x', 164},	/* � */
230    {'C', 'u', 164},	/* � */
231    {'C', 'u', 164},	/* � */
232    {'E', 'u', 164},	/* � */
233    {'Y', '-', 165},	/* � */
234    {'Y', 'e', 165},	/* � */
235    {'|', '|', 166},	/* � */
236    {'B', 'B', 166},	/* � */
237    {'p', 'a', 167},	/* � */
238    {'S', 'E', 167},	/* � */
239    {'"', '"', 168},	/* � */
240    {'\'', ':', 168},	/* � */
241    {'c', 'O', 169},	/* � */
242    {'C', 'o', 169},	/* � */
243    {'a', '-', 170},	/* � */
244    {'<', '<', 171},	/* � */
245    {'-', ',', 172},	/* � */
246    {'N', 'O', 172},	/* � */
247    {'-', '-', 173},	/* � */
248    {'r', 'O', 174},	/* � */
249    {'R', 'g', 174},	/* � */
250    {'-', '=', 175},	/* � */
251    {'\'', 'm', 175},	/* � */
252    {'~', 'o', 176},	/* � */
253    {'D', 'G', 176},	/* � */
254    {'+', '-', 177},	/* � */
255    {'2', '2', 178},	/* � */
256    {'2', 'S', 178},	/* � */
257    {'3', '3', 179},	/* � */
258    {'3', 'S', 179},	/* � */
259    {'\'', '\'', 180},	/* � */
260    {'j', 'u', 181},	/* � */
261    {'M', 'y', 181},	/* � */
262    {'p', 'p', 182},	/* � */
263    {'P', 'I', 182},	/* � */
264    {'~', '.', 183},	/* � */
265    {'.', 'M', 183},	/* � */
266    {',', ',', 184},	/* � */
267    {'\'', ',', 184},	/* � */
268    {'1', '1', 185},	/* � */
269    {'1', 'S', 185},	/* � */
270    {'o', '-', 186},	/* � */
271    {'>', '>', 187},	/* � */
272    {'1', '4', 188},	/* � */
273    {'1', '2', 189},	/* � */
274    {'3', '4', 190},	/* � */
275    {'~', '?', 191},	/* � */
276    {'?', '?', 191},	/* � */
277    {'?', 'I', 191},	/* � */
278    {'A', '`', 192},	/* � */
279    {'A', '!', 192},	/* � */
280    {'A', '\'', 193},	/* � */
281    {'A', '^', 194},	/* � */
282    {'A', '>', 194},	/* � */
283    {'A', '~', 195},	/* � */
284    {'A', '?', 195},	/* � */
285    {'A', '"', 196},	/* � */
286    {'A', ':', 196},	/* � */
287    {'A', '@', 197},	/* � */
288    {'A', 'A', 197},	/* � */
289    {'A', 'E', 198},	/* � */
290    {'C', ',', 199},	/* � */
291    {'E', '`', 200},	/* � */
292    {'E', '!', 200},	/* � */
293    {'E', '\'', 201},	/* � */
294    {'E', '^', 202},	/* � */
295    {'E', '>', 202},	/* � */
296    {'E', '"', 203},	/* � */
297    {'E', ':', 203},	/* � */
298    {'I', '`', 204},	/* � */
299    {'I', '!', 204},	/* � */
300    {'I', '\'', 205},	/* � */
301    {'I', '^', 206},	/* � */
302    {'I', '>', 206},	/* � */
303    {'I', '"', 207},	/* � */
304    {'I', ':', 207},	/* � */
305    {'D', '-', 208},	/* � */
306    {'N', '~', 209},	/* � */
307    {'N', '?', 209},	/* � */
308    {'O', '`', 210},	/* � */
309    {'O', '!', 210},	/* � */
310    {'O', '\'', 211},	/* � */
311    {'O', '^', 212},	/* � */
312    {'O', '>', 212},	/* � */
313    {'O', '~', 213},	/* � */
314    {'O', '?', 213},	/* � */
315    {'O', '"', 214},	/* � */
316    {'O', ':', 214},	/* � */
317    {'/', '\\', 215},	/* � */
318    {'*', 'x', 215},	/* � */
319    {'O', '/', 216},	/* � */
320    {'U', '`', 217},	/* � */
321    {'U', '!', 217},	/* � */
322    {'U', '\'', 218},	/* � */
323    {'U', '^', 219},	/* � */
324    {'U', '>', 219},	/* � */
325    {'U', '"', 220},	/* � */
326    {'U', ':', 220},	/* � */
327    {'Y', '\'', 221},	/* � */
328    {'I', 'p', 222},	/* � */
329    {'T', 'H', 222},	/* � */
330    {'s', 's', 223},	/* � */
331    {'s', '"', 223},	/* � */
332    {'a', '`', 224},	/* � */
333    {'a', '!', 224},	/* � */
334    {'a', '\'', 225},	/* � */
335    {'a', '^', 226},	/* � */
336    {'a', '>', 226},	/* � */
337    {'a', '~', 227},	/* � */
338    {'a', '?', 227},	/* � */
339    {'a', '"', 228},	/* � */
340    {'a', ':', 228},	/* � */
341    {'a', 'a', 229},	/* � */
342    {'a', 'e', 230},	/* � */
343    {'c', ',', 231},	/* � */
344    {'e', '`', 232},	/* � */
345    {'e', '!', 232},	/* � */
346    {'e', '\'', 233},	/* � */
347    {'e', '^', 234},	/* � */
348    {'e', '>', 234},	/* � */
349    {'e', '"', 235},	/* � */
350    {'e', ':', 235},	/* � */
351    {'i', '`', 236},	/* � */
352    {'i', '!', 236},	/* � */
353    {'i', '\'', 237},	/* � */
354    {'i', '^', 238},	/* � */
355    {'i', '>', 238},	/* � */
356    {'i', '"', 239},	/* � */
357    {'i', ':', 239},	/* � */
358    {'d', '-', 240},	/* � */
359    {'n', '~', 241},	/* � */
360    {'n', '?', 241},	/* � */
361    {'o', '`', 242},	/* � */
362    {'o', '!', 242},	/* � */
363    {'o', '\'', 243},	/* � */
364    {'o', '^', 244},	/* � */
365    {'o', '>', 244},	/* � */
366    {'o', '~', 245},	/* � */
367    {'o', '?', 245},	/* � */
368    {'o', '"', 246},	/* � */
369    {'o', ':', 246},	/* � */
370    {':', '-', 247},	/* � */
371    {'o', '/', 248},	/* � */
372    {'u', '`', 249},	/* � */
373    {'u', '!', 249},	/* � */
374    {'u', '\'', 250},	/* � */
375    {'u', '^', 251},	/* � */
376    {'u', '>', 251},	/* � */
377    {'u', '"', 252},	/* � */
378    {'u', ':', 252},	/* � */
379    {'y', '\'', 253},	/* � */
380    {'i', 'p', 254},	/* � */
381    {'t', 'h', 254},	/* � */
382    {'y', '"', 255},	/* � */
383    {'y', ':', 255},	/* � */
384    {'"', '[', 196},	/* � */
385    {'"', '\\', 214},	/* � */
386    {'"', ']', 220},	/* � */
387    {'"', '{', 228},	/* � */
388    {'"', '|', 246},	/* � */
389    {'"', '}', 252},	/* � */
390    {'"', '~', 223}	/* � */
391};
392
393
394char *noargs[1];
395
396void
397InitKeytab()
398{
399  register unsigned int i;
400#ifdef MAPKEYS
401  char *argarr[2];
402#endif
403
404  for (i = 0; i < sizeof(ktab)/sizeof(*ktab); i++)
405    {
406      ktab[i].nr = RC_ILLEGAL;
407      ktab[i].args = noargs;
408      ktab[i].argl = 0;
409    }
410#ifdef MAPKEYS
411  for (i = 0; i < KMAP_KEYS+KMAP_AKEYS; i++)
412    {
413      umtab[i].nr = RC_ILLEGAL;
414      umtab[i].args = noargs;
415      umtab[i].argl = 0;
416      dmtab[i].nr = RC_ILLEGAL;
417      dmtab[i].args = noargs;
418      dmtab[i].argl = 0;
419      mmtab[i].nr = RC_ILLEGAL;
420      mmtab[i].args = noargs;
421      mmtab[i].argl = 0;
422    }
423  argarr[1] = 0;
424  for (i = 0; i < NKMAPDEF; i++)
425    {
426      if (i + KMAPDEFSTART < T_CAPS)
427	continue;
428      if (i + KMAPDEFSTART >= T_CAPS + KMAP_KEYS)
429	continue;
430      if (kmapdef[i] == 0)
431	continue;
432      argarr[0] = kmapdef[i];
433      SaveAction(dmtab + i + (KMAPDEFSTART - T_CAPS), RC_STUFF, argarr, 0);
434    }
435  for (i = 0; i < NKMAPADEF; i++)
436    {
437      if (i + KMAPADEFSTART < T_CURSOR)
438	continue;
439      if (i + KMAPADEFSTART >= T_CURSOR + KMAP_AKEYS)
440	continue;
441      if (kmapadef[i] == 0)
442	continue;
443      argarr[0] = kmapadef[i];
444      SaveAction(dmtab + i + (KMAPADEFSTART - T_CURSOR + KMAP_KEYS), RC_STUFF, argarr, 0);
445    }
446  for (i = 0; i < NKMAPMDEF; i++)
447    {
448      if (i + KMAPMDEFSTART < T_CAPS)
449	continue;
450      if (i + KMAPMDEFSTART >= T_CAPS + KMAP_KEYS)
451	continue;
452      if (kmapmdef[i] == 0)
453	continue;
454      argarr[0] = kmapmdef[i];
455      argarr[1] = 0;
456      SaveAction(mmtab + i + (KMAPMDEFSTART - T_CAPS), RC_STUFF, argarr, 0);
457    }
458#endif
459
460  ktab['h'].nr = RC_HARDCOPY;
461#ifdef BSDJOBS
462  ktab['z'].nr = ktab[Ctrl('z')].nr = RC_SUSPEND;
463#endif
464  ktab['c'].nr = ktab[Ctrl('c')].nr = RC_SCREEN;
465  ktab[' '].nr = ktab[Ctrl(' ')].nr =
466    ktab['n'].nr = ktab[Ctrl('n')].nr = RC_NEXT;
467  ktab['N'].nr = RC_NUMBER;
468  ktab[Ctrl('h')].nr = ktab[0177].nr = ktab['p'].nr = ktab[Ctrl('p')].nr = RC_PREV;
469  ktab['k'].nr = ktab[Ctrl('k')].nr = RC_KILL;
470  ktab['l'].nr = ktab[Ctrl('l')].nr = RC_REDISPLAY;
471  ktab['w'].nr = ktab[Ctrl('w')].nr = RC_WINDOWS;
472  ktab['v'].nr = RC_VERSION;
473  ktab[Ctrl('v')].nr = RC_DIGRAPH;
474  ktab['q'].nr = ktab[Ctrl('q')].nr = RC_XON;
475  ktab['s'].nr = ktab[Ctrl('s')].nr = RC_XOFF;
476  ktab['t'].nr = ktab[Ctrl('t')].nr = RC_TIME;
477  ktab['i'].nr = ktab[Ctrl('i')].nr = RC_INFO;
478  ktab['m'].nr = ktab[Ctrl('m')].nr = RC_LASTMSG;
479  ktab['A'].nr = RC_TITLE;
480#if defined(UTMPOK) && defined(LOGOUTOK)
481  ktab['L'].nr = RC_LOGIN;
482#endif
483  ktab[','].nr = RC_LICENSE;
484  ktab['W'].nr = RC_WIDTH;
485  ktab['.'].nr = RC_DUMPTERMCAP;
486  ktab[Ctrl('\\')].nr = RC_QUIT;
487#ifdef DETACH
488  ktab['d'].nr = ktab[Ctrl('d')].nr = RC_DETACH;
489# ifdef POW_DETACH
490  ktab['D'].nr = RC_POW_DETACH;
491# endif
492#endif
493  ktab['r'].nr = ktab[Ctrl('r')].nr = RC_WRAP;
494  ktab['f'].nr = ktab[Ctrl('f')].nr = RC_FLOW;
495  ktab['C'].nr = RC_CLEAR;
496  ktab['Z'].nr = RC_RESET;
497  ktab['H'].nr = RC_LOG;
498  ktab['M'].nr = RC_MONITOR;
499  ktab['?'].nr = RC_HELP;
500#ifdef MULTI
501  ktab['*'].nr = RC_DISPLAYS;
502#endif
503  {
504    char *args[2];
505    args[0] = "-";
506    args[1] = NULL;
507    SaveAction(ktab + '-', RC_SELECT, args, 0);
508  }
509  for (i = 0; i < ((MAXWIN < 10) ? MAXWIN : 10); i++)
510    {
511      char *args[2], arg1[10];
512      args[0] = arg1;
513      args[1] = 0;
514      sprintf(arg1, "%d", i);
515      SaveAction(ktab + '0' + i, RC_SELECT, args, 0);
516    }
517  ktab['\''].nr = RC_SELECT; /* calling a window by name */
518  {
519    char *args[2];
520    args[0] = "-b";
521    args[1] = 0;
522    SaveAction(ktab + '"', RC_WINDOWLIST, args, 0);
523  }
524  ktab[Ctrl('G')].nr = RC_VBELL;
525  ktab[':'].nr = RC_COLON;
526#ifdef COPY_PASTE
527  ktab['['].nr = ktab[Ctrl('[')].nr = RC_COPY;
528  {
529    char *args[2];
530    args[0] = ".";
531    args[1] = 0;
532    SaveAction(ktab + ']', RC_PASTE, args, 0);
533    SaveAction(ktab + Ctrl(']'), RC_PASTE, args, 0);
534  }
535  ktab['{'].nr = RC_HISTORY;
536  ktab['}'].nr = RC_HISTORY;
537  ktab['>'].nr = RC_WRITEBUF;
538  ktab['<'].nr = RC_READBUF;
539  ktab['='].nr = RC_REMOVEBUF;
540#endif
541#ifdef POW_DETACH
542  ktab['D'].nr = RC_POW_DETACH;
543#endif
544#ifdef LOCK
545  ktab['x'].nr = ktab[Ctrl('x')].nr = RC_LOCKSCREEN;
546#endif
547  ktab['b'].nr = ktab[Ctrl('b')].nr = RC_BREAK;
548  ktab['B'].nr = RC_POW_BREAK;
549  ktab['_'].nr = RC_SILENCE;
550  ktab['S'].nr = RC_SPLIT;
551  ktab['Q'].nr = RC_ONLY;
552  ktab['X'].nr = RC_REMOVE;
553  ktab['F'].nr = RC_FIT;
554  ktab['\t'].nr = RC_FOCUS;
555  /* These come last; they may want overwrite others: */
556  if (DefaultEsc >= 0)
557    {
558      ClearAction(&ktab[DefaultEsc]);
559      ktab[DefaultEsc].nr = RC_OTHER;
560    }
561  if (DefaultMetaEsc >= 0)
562    {
563      ClearAction(&ktab[DefaultMetaEsc]);
564      ktab[DefaultMetaEsc].nr = RC_META;
565    }
566
567  idleaction.nr = RC_BLANKER;
568  idleaction.args = noargs;
569  idleaction.argl = 0;
570}
571
572static struct action *
573FindKtab(class, create)
574char *class;
575int create;
576{
577  struct kclass *kp, **kpp;
578  int i;
579
580  if (class == 0)
581    return ktab;
582  for (kpp = &kclasses; (kp = *kpp) != 0; kpp = &kp->next)
583    if (!strcmp(kp->name, class))
584      break;
585  if (kp == 0)
586    {
587      if (!create)
588	return 0;
589      if (strlen(class) > 80)
590	{
591	  Msg(0, "Command class name too long.");
592	  return 0;
593	}
594      kp = malloc(sizeof(*kp));
595      if (kp == 0)
596	{
597	  Msg(0, strnomem);
598	  return 0;
599	}
600      kp->name = SaveStr(class);
601      for (i = 0; i < (int)(sizeof(kp->ktab)/sizeof(*kp->ktab)); i++)
602	{
603	  kp->ktab[i].nr = RC_ILLEGAL;
604	  kp->ktab[i].args = noargs;
605	}
606      kp->next = 0;
607      *kpp = kp;
608    }
609  return kp->ktab;
610}
611
612static void
613ClearAction(act)
614struct action *act;
615{
616  char **p;
617
618  if (act->nr == RC_ILLEGAL)
619    return;
620  act->nr = RC_ILLEGAL;
621  if (act->args == noargs)
622    return;
623  for (p = act->args; *p; p++)
624    free(*p);
625  free((char *)act->args);
626  act->args = noargs;
627  act->argl = 0;
628}
629
630/*
631 * ProcessInput: process input from display and feed it into
632 * the layer on canvas D_forecv.
633 */
634
635#ifdef MAPKEYS
636
637/*
638 *  This ProcessInput just does the keybindings and passes
639 *  everything else on to ProcessInput2.
640 */
641
642void
643ProcessInput(ibuf, ilen)
644char *ibuf;
645int ilen;
646{
647  int ch, slen;
648  unsigned char *s, *q;
649  int i, l;
650  char *p;
651
652  debug1("ProcessInput: %d bytes\n", ilen);
653  if (display == 0 || ilen == 0)
654    return;
655  if (D_seql)
656    evdeq(&D_mapev);
657  slen = ilen;
658  s = (unsigned char *)ibuf;
659  while (ilen-- > 0)
660    {
661      ch = *s++;
662      if (D_dontmap || !D_nseqs)
663	{
664          D_dontmap = 0;
665	  continue;
666	}
667      for (;;)
668	{
669	  debug3("cmp %c %c[%d]\n", ch, *D_seqp, D_seqp - D_kmaps);
670	  if (*D_seqp != ch)
671	    {
672	      l = D_seqp[D_seqp[-D_seql-1] + 1];
673	      if (l)
674		{
675		  D_seqp += l * 2 + 4;
676		  debug1("miss %d\n", D_seqp - D_kmaps);
677		  continue;
678		}
679	      debug("complete miss\n");
680	      D_mapdefault = 0;
681	      l = D_seql;
682	      p = (char *)D_seqp - l;
683	      D_seql = 0;
684	      D_seqp = D_kmaps + 3;
685	      if (l == 0)
686		break;
687	      if ((q = D_seqh) != 0)
688		{
689		  D_seqh = 0;
690		  i = q[0] << 8 | q[1];
691		  i &= ~KMAP_NOTIMEOUT;
692		  debug1("Mapping former hit #%d - ", i);
693		  debug2("%d(%s) - ", q[2], q + 3);
694		  if (StuffKey(i))
695		    ProcessInput2((char *)q + 3, q[2]);
696		  if (display == 0)
697		    return;
698		  l -= q[2];
699		  p += q[2];
700		}
701	      else
702	        D_dontmap = 1;
703	      debug1("flush old %d\n", l);
704	      ProcessInput(p, l);
705	      if (display == 0)
706		return;
707	      evdeq(&D_mapev);
708	      continue;
709	    }
710	  if (D_seql++ == 0)
711	    {
712	      /* Finish old stuff */
713	      slen -= ilen + 1;
714	      debug1("finish old %d\n", slen);
715	      if (slen)
716	        ProcessInput2(ibuf, slen);
717	      if (display == 0)
718		return;
719	      D_seqh = 0;
720	    }
721	  ibuf = (char *)s;
722	  slen = ilen;
723	  D_seqp++;
724	  l = D_seql;
725	  debug2("length am %d, want %d\n", l, D_seqp[-l - 1]);
726	  if (l == D_seqp[-l - 1])
727	    {
728	      if (D_seqp[l] != l)
729		{
730		  q = D_seqp + 1 + l;
731		  if (D_kmaps + D_nseqs > q && q[2] > l && !bcmp(D_seqp - l, q + 3, l))
732		    {
733		      debug1("have another mapping (%s), delay execution\n", q + 3);
734		      D_seqh = D_seqp - 3 - l;
735		      D_seqp = q + 3 + l;
736		      break;
737		    }
738		}
739	      i = D_seqp[-l - 3] << 8 | D_seqp[-l - 2];
740	      i &= ~KMAP_NOTIMEOUT;
741	      debug1("Mapping #%d - ", i);
742	      p = (char *)D_seqp - l;
743	      debug2("%d(%s) - ", l, p);
744	      D_seql = 0;
745	      D_seqp = D_kmaps + 3;
746	      D_seqh = 0;
747	      if (StuffKey(i))
748		ProcessInput2(p, l);
749	      if (display == 0)
750		return;
751	    }
752	  break;
753	}
754    }
755  if (D_seql)
756    {
757      debug("am in sequence -> check for timeout\n");
758      l = D_seql;
759      for (s = D_seqp; ; s += i * 2 + 4)
760	{
761	  if (s[-l-3] & KMAP_NOTIMEOUT >> 8)
762	    break;
763	  if ((i = s[s[-l-1] + 1]) == 0)
764	    {
765	      SetTimeout(&D_mapev, maptimeout);
766	      evenq(&D_mapev);
767	      break;
768	    }
769	}
770    }
771  ProcessInput2(ibuf, slen);
772}
773
774#else
775# define ProcessInput2 ProcessInput
776#endif
777
778
779/*
780 *  Here only the screen escape commands are handled.
781 */
782
783void
784ProcessInput2(ibuf, ilen)
785char *ibuf;
786int ilen;
787{
788  char *s;
789  int ch, slen;
790  struct action *ktabp;
791
792  debug1("ProcessInput2: %d bytes\n", ilen);
793  while (ilen && display)
794    {
795      debug1(" - ilen now %d bytes\n", ilen);
796      flayer = D_forecv->c_layer;
797      fore = D_fore;
798      slen = ilen;
799      s = ibuf;
800      if (!D_ESCseen)
801	{
802	  while (ilen > 0)
803	    {
804	      if ((unsigned char)*s++ == D_user->u_Esc)
805		break;
806	      ilen--;
807	    }
808	  slen -= ilen;
809	  if (slen)
810	    DoProcess(fore, &ibuf, &slen, 0);
811	  if (--ilen == 0)
812	    D_ESCseen = ktab;
813	}
814      if (ilen <= 0)
815        return;
816      ktabp = D_ESCseen ? D_ESCseen : ktab;
817      D_ESCseen = 0;
818      ch = (unsigned char)*s;
819
820      /*
821       * As users have different esc characters, but a common ktab[],
822       * we fold back the users esc and meta-esc key to the Default keys
823       * that can be looked up in the ktab[]. grmbl. jw.
824       * XXX: make ktab[] a per user thing.
825       */
826      if (ch == D_user->u_Esc)
827        ch = DefaultEsc;
828      else if (ch == D_user->u_MetaEsc)
829        ch = DefaultMetaEsc;
830
831      if (ch >= 0)
832        DoAction(&ktabp[ch], ch);
833      ibuf = (char *)(s + 1);
834      ilen--;
835    }
836}
837
838void
839DoProcess(p, bufp, lenp, pa)
840struct win *p;
841char **bufp;
842int *lenp;
843struct paster *pa;
844{
845  int oldlen;
846  struct display *d = display;
847
848#ifdef COPY_PASTE
849  /* XXX -> PasteStart */
850  if (pa && *lenp > 1 && p && p->w_slowpaste)
851    {
852      /* schedule slowpaste event */
853      SetTimeout(&p->w_paster.pa_slowev, p->w_slowpaste);
854      evenq(&p->w_paster.pa_slowev);
855      return;
856    }
857#endif
858  while (flayer && *lenp)
859    {
860#ifdef COPY_PASTE
861      if (!pa && p && p->w_paster.pa_pastelen && flayer == p->w_paster.pa_pastelayer)
862	{
863	  debug("layer is busy - beep!\n");
864	  WBell(p, visual_bell);
865	  *bufp += *lenp;
866	  *lenp = 0;
867	  display = d;
868	  return;
869	}
870#endif
871      oldlen = *lenp;
872      LayProcess(bufp, lenp);
873#ifdef COPY_PASTE
874      if (pa && !pa->pa_pastelayer)
875	break;		/* flush rest of paste */
876#endif
877      if (*lenp == oldlen)
878	{
879	  if (pa)
880	    {
881	      display = d;
882	      return;
883	    }
884	  /* We're full, let's beep */
885	  debug("layer is full - beep!\n");
886	  WBell(p, visual_bell);
887	  break;
888	}
889    }
890  *bufp += *lenp;
891  *lenp = 0;
892  display = d;
893#ifdef COPY_PASTE
894  if (pa && pa->pa_pastelen == 0)
895    FreePaster(pa);
896#endif
897}
898
899int
900FindCommnr(str)
901char *str;
902{
903  int x, m, l = 0, r = RC_LAST;
904  while (l <= r)
905    {
906      m = (l + r) / 2;
907      x = strcmp(str, comms[m].name);
908      if (x > 0)
909	l = m + 1;
910      else if (x < 0)
911	r = m - 1;
912      else
913	return m;
914    }
915  return RC_ILLEGAL;
916}
917
918static int
919CheckArgNum(nr, args)
920int nr;
921char **args;
922{
923  int i, n;
924  static char *argss[] = {"no", "one", "two", "three", "four", "OOPS"};
925  static char *orformat[] =
926    {
927      "%s: %s: %s argument%s required",
928      "%s: %s: %s or %s argument%s required",
929      "%s: %s: %s, %s or %s argument%s required",
930      "%s: %s: %s, %s, %s or %s argument%s required"
931    };
932
933  n = comms[nr].flags & ARGS_MASK;
934  for (i = 0; args[i]; i++)
935    ;
936  if (comms[nr].flags & ARGS_ORMORE)
937    {
938      if (i < n)
939	{
940	  Msg(0, "%s: %s: at least %s argument%s required",
941	      rc_name, comms[nr].name, argss[n], n != 1 ? "s" : "");
942	  return -1;
943	}
944    }
945  else if ((comms[nr].flags & ARGS_PLUS1) &&
946           (comms[nr].flags & ARGS_PLUS2) &&
947	   (comms[nr].flags & ARGS_PLUS3))
948    {
949      if (i != n && i != n + 1 && i != n + 2 && i != n + 3)
950        {
951	  Msg(0, orformat[3], rc_name, comms[nr].name, argss[n],
952	      argss[n + 1], argss[n + 2], argss[n + 3], "");
953	  return -1;
954	}
955    }
956  else if ((comms[nr].flags & ARGS_PLUS1) &&
957           (comms[nr].flags & ARGS_PLUS2))
958    {
959      if (i != n && i != n + 1 && i != n + 2)
960	{
961	  Msg(0, orformat[2], rc_name, comms[nr].name, argss[n],
962	      argss[n + 1], argss[n + 2], "");
963          return -1;
964	}
965    }
966  else if ((comms[nr].flags & ARGS_PLUS1) &&
967           (comms[nr].flags & ARGS_PLUS3))
968    {
969      if (i != n && i != n + 1 && i != n + 3)
970        {
971	  Msg(0, orformat[2], rc_name, comms[nr].name, argss[n],
972	      argss[n + 1], argss[n + 3], "");
973	  return -1;
974	}
975    }
976  else if ((comms[nr].flags & ARGS_PLUS2) &&
977           (comms[nr].flags & ARGS_PLUS3))
978    {
979      if (i != n && i != n + 2 && i != n + 3)
980        {
981	  Msg(0, orformat[2], rc_name, comms[nr].name, argss[n],
982	      argss[n + 2], argss[n + 3], "");
983	  return -1;
984	}
985    }
986  else if (comms[nr].flags & ARGS_PLUS1)
987    {
988      if (i != n && i != n + 1)
989        {
990	  Msg(0, orformat[1], rc_name, comms[nr].name, argss[n],
991	      argss[n + 1], n != 0 ? "s" : "");
992	  return -1;
993	}
994    }
995  else if (comms[nr].flags & ARGS_PLUS2)
996    {
997      if (i != n && i != n + 2)
998        {
999	  Msg(0, orformat[1], rc_name, comms[nr].name, argss[n],
1000	      argss[n + 2], "s");
1001	  return -1;
1002	}
1003    }
1004  else if (comms[nr].flags & ARGS_PLUS3)
1005    {
1006      if (i != n && i != n + 3)
1007        {
1008	  Msg(0, orformat[1], rc_name, comms[nr].name, argss[n],
1009	      argss[n + 3], "");
1010	  return -1;
1011	}
1012    }
1013  else if (i != n)
1014    {
1015      Msg(0, orformat[0], rc_name, comms[nr].name, argss[n], n != 1 ? "s" : "");
1016      return -1;
1017    }
1018  return i;
1019}
1020
1021/*ARGSUSED*/
1022void
1023DoAction(act, key)
1024struct action *act;
1025int key;
1026{
1027  int nr = act->nr;
1028  char **args = act->args;
1029  int *argl = act->argl;
1030  struct win *p;
1031  int argc, i, n, msgok;
1032  char *s;
1033  char ch;
1034  struct display *odisplay = display;
1035  struct acluser *user;
1036
1037  user = display ? D_user : users;
1038  if (nr == RC_ILLEGAL)
1039    {
1040      debug1("key '%c': No action\n", key);
1041      return;
1042    }
1043  n = comms[nr].flags;
1044  if ((n & NEED_DISPLAY) && display == 0)
1045    {
1046      Msg(0, "%s: %s: display required", rc_name, comms[nr].name);
1047      return;
1048    }
1049  if ((n & NEED_FORE) && fore == 0)
1050    {
1051      Msg(0, "%s: %s: window required", rc_name, comms[nr].name);
1052      return;
1053    }
1054  if ((n & NEED_LAYER) && flayer == 0)
1055    {
1056      Msg(0, "%s: %s: display or window required", rc_name, comms[nr].name);
1057      return;
1058    }
1059  if ((argc = CheckArgNum(nr, args)) < 0)
1060    return;
1061#ifdef MULTIUSER
1062  if (display)
1063    {
1064      if (AclCheckPermCmd(D_user, ACL_EXEC, &comms[nr]))
1065        {
1066	  Msg(0, "%s: %s: permission denied (user %s)",
1067	      rc_name, comms[nr].name, (EffectiveAclUser ? EffectiveAclUser : D_user)->u_name);
1068	  return;
1069	}
1070    }
1071#endif /* MULTIUSER */
1072
1073  msgok = display && !*rc_name;
1074  switch(nr)
1075    {
1076    case RC_SELECT:
1077      if (!*args)
1078        InputSelect();
1079      else if (args[0][0] == '-' && !args[0][1])
1080	{
1081	  SetForeWindow((struct win *)0);
1082	  Activate(0);
1083	}
1084      else if (args[0][0] == '.' && !args[0][1])
1085	{
1086	  if (!fore)
1087	    Msg(0, "select . needs a window");
1088	  else
1089	    {
1090	      SetForeWindow(fore);
1091	      Activate(0);
1092	    }
1093	}
1094      else if (ParseWinNum(act, &n) == 0)
1095        SwitchWindow(n);
1096      break;
1097#ifdef AUTO_NUKE
1098    case RC_DEFAUTONUKE:
1099      if (ParseOnOff(act, &defautonuke) == 0 && msgok)
1100	Msg(0, "Default autonuke turned %s", defautonuke ? "on" : "off");
1101      if (display && *rc_name)
1102	D_auto_nuke = defautonuke;
1103      break;
1104    case RC_AUTONUKE:
1105      if (ParseOnOff(act, &D_auto_nuke) == 0 && msgok)
1106	Msg(0, "Autonuke turned %s", D_auto_nuke ? "on" : "off");
1107      break;
1108#endif
1109    case RC_DEFOBUFLIMIT:
1110      if (ParseNum(act, &defobuflimit) == 0 && msgok)
1111	Msg(0, "Default limit set to %d", defobuflimit);
1112      if (display && *rc_name)
1113	{
1114	  D_obufmax = defobuflimit;
1115	  D_obuflenmax = D_obuflen - D_obufmax;
1116	}
1117      break;
1118    case RC_OBUFLIMIT:
1119      if (*args == 0)
1120	Msg(0, "Limit is %d, current buffer size is %d", D_obufmax, D_obuflen);
1121      else if (ParseNum(act, &D_obufmax) == 0 && msgok)
1122	Msg(0, "Limit set to %d", D_obufmax);
1123      D_obuflenmax = D_obuflen - D_obufmax;
1124      break;
1125    case RC_DUMPTERMCAP:
1126      WriteFile(user, (char *)0, DUMP_TERMCAP);
1127      break;
1128    case RC_HARDCOPY:
1129      {
1130	int mode = DUMP_HARDCOPY;
1131
1132	if (argc > 1 && !strcmp(*args, "-h"))
1133	  {
1134	    mode = DUMP_SCROLLBACK;
1135	    args++;
1136	    argc--;
1137	  }
1138	if (*args && args[1])
1139	  {
1140	    Msg(0, "%s: hardcopy: too many arguments", rc_name);
1141	    break;
1142	  }
1143        if (fore == 0 && *args == 0)
1144	  Msg(0, "%s: hardcopy: window required", rc_name);
1145        else
1146          WriteFile(user, *args, mode);
1147      }
1148      break;
1149    case RC_DEFLOG:
1150      (void)ParseOnOff(act, &nwin_default.Lflag);
1151      break;
1152    case RC_LOG:
1153      n = fore->w_log ? 1 : 0;
1154      ParseSwitch(act, &n);
1155      LogToggle(n);
1156      break;
1157#ifdef BSDJOBS
1158    case RC_SUSPEND:
1159      Detach(D_STOP);
1160      break;
1161#endif
1162    case RC_NEXT:
1163      if (MoreWindows())
1164	SwitchWindow(NextWindow());
1165      break;
1166    case RC_PREV:
1167      if (MoreWindows())
1168	SwitchWindow(PreviousWindow());
1169      break;
1170    case RC_KILL:
1171      {
1172	char *name;
1173
1174	if (key >= 0)
1175	  {
1176#ifdef PSEUDOS
1177	    Input(fore->w_pwin ? "Really kill this filter [y/n]" : "Really kill this window [y/n]", 1, INP_RAW, confirm_fn, (char *)RC_KILL);
1178#else
1179	    Input("Really kill this window [y/n]", 1, INP_RAW, confirm_fn, (char *)RC_KILL);
1180#endif
1181	    break;
1182	  }
1183	n = fore->w_number;
1184#ifdef PSEUDOS
1185	if (fore->w_pwin)
1186	  {
1187	    FreePseudowin(fore);
1188	    Msg(0, "Filter removed.");
1189	    break;
1190	  }
1191#endif
1192	name = SaveStr(fore->w_title);
1193	KillWindow(fore);
1194	Msg(0, "Window %d (%s) killed.", n, name);
1195	if (name)
1196	  free(name);
1197	break;
1198      }
1199    case RC_QUIT:
1200      if (key >= 0)
1201	{
1202	  Input("Really quit and kill all your windows [y/n]", 1, INP_RAW, confirm_fn, (char *)RC_QUIT);
1203	  break;
1204	}
1205      Finit(0);
1206      /* NOTREACHED */
1207#ifdef DETACH
1208    case RC_DETACH:
1209      if (*args && !strcmp(*args, "-h"))
1210        Hangup();
1211      else
1212        Detach(D_DETACH);
1213      break;
1214# ifdef POW_DETACH
1215    case RC_POW_DETACH:
1216      if (key >= 0)
1217	{
1218	  static char buf[2];
1219
1220	  buf[0] = key;
1221	  Input(buf, 1, INP_RAW, pow_detach_fn, NULL);
1222	}
1223      else
1224        Detach(D_POWER); /* detach and kill Attacher's parent */
1225      break;
1226# endif
1227#endif
1228    case RC_DEBUG:
1229#ifdef DEBUG
1230      if (!*args)
1231        {
1232	  if (dfp)
1233	    Msg(0, "debugging info is written to %s/", DEBUGDIR);
1234	  else
1235	    Msg(0, "debugging is currently off. Use 'debug on' to enable.");
1236	  break;
1237	}
1238      if (dfp)
1239        {
1240	  debug("debug: closing debug file.\n");
1241	  fflush(dfp);
1242	  fclose(dfp);
1243	  dfp = NULL;
1244	}
1245      if (strcmp("off", *args))
1246        opendebug(0, 1);
1247# ifdef SIG_NODEBUG
1248      else if (display)
1249        kill(D_userpid, SIG_NODEBUG);	/* a one shot item, but hey... */
1250# endif /* SIG_NODEBUG */
1251#else
1252      if (*args == 0 || strcmp("off", *args))
1253        Msg(0, "Sorry, screen was compiled without -DDEBUG option.");
1254#endif
1255      break;
1256#ifdef ZMODEM
1257    case RC_ZMODEM:
1258      if (*args && !strcmp(*args, "sendcmd"))
1259	{
1260	  if (args[1])
1261	    {
1262	      free(zmodem_sendcmd);
1263	      zmodem_sendcmd = SaveStr(args[1]);
1264	    }
1265	  if (msgok)
1266	    Msg(0, "zmodem sendcmd: %s", zmodem_sendcmd);
1267	  break;
1268	}
1269      if (*args && !strcmp(*args, "recvcmd"))
1270	{
1271	  if (args[1])
1272	    {
1273	      free(zmodem_recvcmd);
1274	      zmodem_recvcmd = SaveStr(args[1]);
1275	    }
1276	  if (msgok)
1277	    Msg(0, "zmodem recvcmd: %s", zmodem_recvcmd);
1278	  break;
1279	}
1280      if (*args)
1281	{
1282	  for (i = 0; i < 4; i++)
1283	    if (!strcmp(zmodes[i], *args))
1284	      break;
1285	  if (i == 4 && !strcmp(*args, "on"))
1286	    i = 1;
1287	  if (i == 4)
1288	    {
1289	      Msg(0, "usage: zmodem off|auto|catch|pass");
1290	      break;
1291	    }
1292	  zmodem_mode = i;
1293	}
1294      if (msgok)
1295	Msg(0, "zmodem mode is %s", zmodes[zmodem_mode]);
1296      break;
1297#endif
1298    case RC_ZOMBIE:
1299      {
1300        if (!(s = *args))
1301          {
1302            ZombieKey_destroy = 0;
1303            break;
1304          }
1305	if (*argl == 0 || *argl > 2)
1306	  {
1307	    Msg(0, "%s:zombie: one or two characters expected.", rc_name);
1308	    break;
1309	  }
1310        ZombieKey_destroy = args[0][0];
1311        ZombieKey_resurrect = *argl == 2 ? args[0][1] : 0;
1312      }
1313      break;
1314    case RC_WALL:
1315#ifdef MULTIUSER
1316      s = D_user->u_name;
1317#else
1318      s = D_usertty;
1319#endif
1320        {
1321	  struct display *olddisplay = display;
1322          display = 0;		/* no display will cause a broadcast */
1323          Msg(0, "%s: %s", s, *args);
1324	  display = olddisplay;
1325        }
1326      break;
1327    case RC_AT:
1328      /* where this AT command comes from: */
1329#ifdef MULTIUSER
1330      s = SaveStr(D_user->u_name);
1331      /* DO NOT RETURN FROM HERE WITHOUT RESETTING THIS: */
1332      EffectiveAclUser = D_user;
1333#else
1334      s = SaveStr(D_usertty);
1335#endif
1336      n = strlen(args[0]);
1337      if (n) n--;
1338      /*
1339       * the windows/displays loops are quite dangerous here, take extra
1340       * care not to trigger landmines. Things may appear/disappear while
1341       * we are walking along.
1342       */
1343      switch (args[0][n])
1344        {
1345	case '*':		/* user */
1346	  {
1347	    struct display *nd;
1348	    struct acluser *u;
1349
1350	    if (!n)
1351	      u = D_user;
1352	    else
1353	      for (u = users; u; u = u->u_next)
1354	        {
1355		  debug3("strncmp('%s', '%s', %d)\n", *args, u->u_name, n);
1356		  if (!strncmp(*args, u->u_name, n))
1357		    break;
1358	        }
1359	    debug1("at all displays of user %s\n", u->u_name);
1360	    for (display = displays; display; display = nd)
1361	      {
1362		nd = display->d_next;
1363		if (D_forecv == 0)
1364		  continue;
1365		flayer = D_forecv->c_layer;
1366		fore = D_fore;
1367	        if (D_user != u)
1368		  continue;
1369		debug1("AT display %s\n", D_usertty);
1370		DoCommand(args + 1, argl + 1);
1371		if (display)
1372		  Msg(0, "command from %s: %s %s",
1373		      s, args[1], args[2] ? args[2] : "");
1374		display = NULL;
1375		flayer = 0;
1376		fore = NULL;
1377	      }
1378	    break;
1379	  }
1380	case '%':		/* display */
1381	  {
1382	    struct display *nd;
1383
1384	    debug1("at display matching '%s'\n", args[0]);
1385	    for (display = displays; display; display = nd)
1386	      {
1387	        nd = display->d_next;
1388		if (D_forecv == 0)
1389		  continue;
1390		fore = D_fore;
1391		flayer = D_forecv->c_layer;
1392	        if (strncmp(args[0], D_usertty, n) &&
1393		    (strncmp("/dev/", D_usertty, 5) ||
1394		     strncmp(args[0], D_usertty + 5, n)) &&
1395		    (strncmp("/dev/tty", D_usertty, 8) ||
1396		     strncmp(args[0], D_usertty + 8, n)))
1397		  continue;
1398		debug1("AT display %s\n", D_usertty);
1399		DoCommand(args + 1, argl + 1);
1400		if (display)
1401		  Msg(0, "command from %s: %s %s",
1402		      s, args[1], args[2] ? args[2] : "");
1403		display = NULL;
1404		fore = NULL;
1405		flayer = 0;
1406	      }
1407	    break;
1408	  }
1409	case '#':		/* window */
1410	  n--;
1411	  /* FALLTHROUGH */
1412	default:
1413	  {
1414	    struct win *nw;
1415	    int ch;
1416
1417	    n++;
1418	    ch = args[0][n];
1419	    args[0][n] = '\0';
1420	    if (!*args[0] || (i = WindowByNumber(args[0])) < 0)
1421	      {
1422	        args[0][n] = ch;      /* must restore string in case of bind */
1423	        /* try looping over titles */
1424		for (fore = windows; fore; fore = nw)
1425		  {
1426		    nw = fore->w_next;
1427		    if (strncmp(args[0], fore->w_title, n))
1428		      continue;
1429		    debug2("AT window %d(%s)\n", fore->w_number, fore->w_title);
1430		    /*
1431		     * consider this a bug or a feature:
1432		     * while looping through windows, we have fore AND
1433		     * display context. This will confuse users who try to
1434		     * set up loops inside of loops, but often allows to do
1435		     * what you mean, even when you adress your context wrong.
1436		     */
1437		    i = 0;
1438		    /* XXX: other displays? */
1439		    if (fore->w_layer.l_cvlist)
1440		      display = fore->w_layer.l_cvlist->c_display;
1441		    flayer = fore->w_savelayer ? fore->w_savelayer : &fore->w_layer;
1442		    DoCommand(args + 1, argl + 1);	/* may destroy our display */
1443		    if (fore && fore->w_layer.l_cvlist)
1444		      {
1445		        display = fore->w_layer.l_cvlist->c_display;
1446		        Msg(0, "command from %s: %s %s",
1447			    s, args[1], args[2] ? args[2] : "");
1448		      }
1449		  }
1450		display = NULL;
1451		fore = NULL;
1452		if (i < 0)
1453		  Msg(0, "%s: at '%s': no such window.\n", rc_name, args[0]);
1454		break;
1455	      }
1456	    else if (i < MAXWIN && (fore = wtab[i]))
1457	      {
1458	        args[0][n] = ch;      /* must restore string in case of bind */
1459	        debug2("AT window %d (%s)\n", fore->w_number, fore->w_title);
1460		if (fore->w_layer.l_cvlist)
1461		  display = fore->w_layer.l_cvlist->c_display;
1462		flayer = fore->w_savelayer ? fore->w_savelayer : &fore->w_layer;
1463		DoCommand(args + 1, argl + 1);
1464		if (fore && fore->w_layer.l_cvlist)
1465		  {
1466		    display = fore->w_layer.l_cvlist->c_display;
1467		    Msg(0, "command from %s: %s %s",
1468		        s, args[1], args[2] ? args[2] : "");
1469		  }
1470		display = NULL;
1471		fore = NULL;
1472	      }
1473	    else
1474	      Msg(0, "%s: at [identifier][%%|*|#] command [args]", rc_name);
1475	    break;
1476	  }
1477	}
1478      free(s);
1479#ifdef MULTIUSER
1480      EffectiveAclUser = NULL;
1481#endif
1482      break;
1483
1484#ifdef COPY_PASTE
1485    case RC_READREG:
1486#ifdef ENCODINGS
1487      i = fore ? fore->w_encoding : display ? display->d_encoding : 0;
1488      if (args[0] && args[1] && !strcmp(args[0], "-e"))
1489	{
1490	  i = FindEncoding(args[1]);
1491	  if (i == -1)
1492	    {
1493	      Msg(0, "%s: readreg: unknown encoding", rc_name);
1494	      break;
1495	    }
1496	  args += 2;
1497	}
1498#endif
1499      /*
1500       * Without arguments we prompt for a destination register.
1501       * It will receive the copybuffer contents.
1502       * This is not done by RC_PASTE, as we prompt for source
1503       * (not dest) there.
1504       */
1505      if ((s = *args) == NULL)
1506	{
1507	  Input("Copy to register:", 1, INP_RAW, copy_reg_fn, NULL);
1508	  break;
1509	}
1510      if (*argl != 1)
1511	{
1512	  Msg(0, "%s: copyreg: character, ^x, or (octal) \\032 expected.", rc_name);
1513	  break;
1514	}
1515      ch = args[0][0];
1516      /*
1517       * With two arguments we *really* read register contents from file
1518       */
1519      if (args[1])
1520        {
1521	  if (args[2])
1522	    {
1523	      Msg(0, "%s: readreg: too many arguments", rc_name);
1524	      break;
1525	    }
1526	  if ((s = ReadFile(args[1], &n)))
1527	    {
1528	      struct plop *pp = plop_tab + (int)(unsigned char)ch;
1529
1530	      if (pp->buf)
1531		free(pp->buf);
1532	      pp->buf = s;
1533	      pp->len = n;
1534#ifdef ENCODINGS
1535	      pp->enc = i;
1536#endif
1537	    }
1538	}
1539      else
1540        /*
1541	 * with one argument we copy the copybuffer into a specified register
1542	 * This could be done with RC_PASTE too, but is here to be consistent
1543	 * with the zero argument call.
1544	 */
1545        copy_reg_fn(&ch, 0, NULL);
1546      break;
1547#endif
1548    case RC_REGISTER:
1549#ifdef ENCODINGS
1550      i = fore ? fore->w_encoding : display ? display->d_encoding : 0;
1551      if (args[0] && args[1] && !strcmp(args[0], "-e"))
1552	{
1553	  i = FindEncoding(args[1]);
1554	  if (i == -1)
1555	    {
1556	      Msg(0, "%s: register: unknown encoding", rc_name);
1557	      break;
1558	    }
1559	  args += 2;
1560	  argc -= 2;
1561	}
1562#endif
1563      if (argc != 2)
1564	{
1565	  Msg(0, "%s: register: illegal number of arguments.", rc_name);
1566	  break;
1567	}
1568      if (*argl != 1)
1569	{
1570	  Msg(0, "%s: register: character, ^x, or (octal) \\032 expected.", rc_name);
1571	  break;
1572	}
1573      ch = args[0][0];
1574#ifdef COPY_PASTE
1575      if (ch == '.')
1576	{
1577	  if (user->u_plop.buf != NULL)
1578	    UserFreeCopyBuffer(user);
1579	  if (args[1] && args[1][0])
1580	    {
1581	      user->u_plop.buf = SaveStrn(args[1], argl[1]);
1582	      user->u_plop.len = argl[1];
1583#ifdef ENCODINGS
1584	      user->u_plop.enc = i;
1585#endif
1586	    }
1587	}
1588#endif
1589      else
1590	{
1591	  struct plop *plp = plop_tab + (int)(unsigned char)ch;
1592
1593	  if (plp->buf)
1594	    free(plp->buf);
1595	  plp->buf = SaveStrn(args[1], argl[1]);
1596	  plp->len = argl[1];
1597#ifdef ENCODINGS
1598	  plp->enc = i;
1599#endif
1600	}
1601      break;
1602    case RC_PROCESS:
1603      if ((s = *args) == NULL)
1604	{
1605	  Input("Process register:", 1, INP_RAW, process_fn, NULL);
1606	  break;
1607	}
1608      if (*argl != 1)
1609	{
1610	  Msg(0, "%s: process: character, ^x, or (octal) \\032 expected.", rc_name);
1611	  break;
1612	}
1613      ch = args[0][0];
1614      process_fn(&ch, 0, NULL);
1615      break;
1616    case RC_STUFF:
1617      s = *args;
1618      n = *argl;
1619      if (args[1])
1620	{
1621	  if (strcmp(s, "-k"))
1622	    {
1623	      Msg(0, "%s: stuff: invalid option %s", rc_name, s);
1624	      break;
1625	    }
1626	  s = args[1];
1627	  for (i = T_CAPS; i < T_OCAPS; i++)
1628	    if (strcmp(term[i].tcname, s) == 0)
1629	      break;
1630	  if (i == T_OCAPS)
1631	    {
1632	      Msg(0, "%s: stuff: unknown key '%s'", rc_name, s);
1633	      break;
1634	    }
1635#ifdef MAPKEYS
1636	  if (StuffKey(i - T_CAPS) == 0)
1637	    break;
1638#endif
1639	  s = display ? D_tcs[i].str : 0;
1640	  if (s == 0)
1641	    break;
1642	  n = strlen(s);
1643	}
1644      while(n)
1645        LayProcess(&s, &n);
1646      break;
1647    case RC_REDISPLAY:
1648      Activate(-1);
1649      break;
1650    case RC_WINDOWS:
1651      ShowWindows(-1);
1652      break;
1653    case RC_VERSION:
1654      Msg(0, "screen %s", version);
1655      break;
1656    case RC_TIME:
1657      if (*args)
1658	{
1659	  timestring = SaveStr(*args);
1660	  break;
1661	}
1662      Msg(0, "%s", MakeWinMsg(timestring, fore, '%'));
1663      break;
1664    case RC_INFO:
1665      ShowInfo();
1666      break;
1667    case RC_DINFO:
1668      ShowDInfo();
1669      break;
1670    case RC_COMMAND:
1671	{
1672	  struct action *ktabp = ktab;
1673	  if (argc == 2 && !strcmp(*args, "-c"))
1674	    {
1675	      if ((ktabp = FindKtab(args[1], 0)) == 0)
1676		{
1677		  Msg(0, "Unknown command class '%s'", args[1]);
1678		  break;
1679		}
1680	    }
1681	  if (D_ESCseen != ktab || ktabp != ktab)
1682	    {
1683	      D_ESCseen = ktabp;
1684	      break;
1685	    }
1686	  D_ESCseen = 0;
1687	}
1688      /* FALLTHROUGH */
1689    case RC_OTHER:
1690      if (MoreWindows())
1691	SwitchWindow(display && D_other ? D_other->w_number : NextWindow());
1692      break;
1693    case RC_META:
1694      if (user->u_Esc == -1)
1695        break;
1696      ch = user->u_Esc;
1697      s = &ch;
1698      n = 1;
1699      LayProcess(&s, &n);
1700      break;
1701    case RC_XON:
1702      ch = Ctrl('q');
1703      s = &ch;
1704      n = 1;
1705      LayProcess(&s, &n);
1706      break;
1707    case RC_XOFF:
1708      ch = Ctrl('s');
1709      s = &ch;
1710      n = 1;
1711      LayProcess(&s, &n);
1712      break;
1713    case RC_DEFBREAKTYPE:
1714    case RC_BREAKTYPE:
1715	{
1716	  static char *types[] = { "TIOCSBRK", "TCSBRK", "tcsendbreak", NULL };
1717	  extern int breaktype;
1718
1719	  if (*args)
1720	    {
1721	      if (ParseNum(act, &n))
1722		for (n = 0; n < (int)(sizeof(types)/sizeof(*types)); n++)
1723		  {
1724		    for (i = 0; i < 4; i++)
1725		      {
1726			ch = args[0][i];
1727			if (ch >= 'a' && ch <= 'z')
1728			  ch -= 'a' - 'A';
1729			if (ch != types[n][i] && (ch + ('a' - 'A')) != types[n][i])
1730			  break;
1731		      }
1732		    if (i == 4)
1733		      break;
1734		  }
1735	      if (n < 0 || n >= (int)(sizeof(types)/sizeof(*types)))
1736	        Msg(0, "%s invalid, chose one of %s, %s or %s", *args, types[0], types[1], types[2]);
1737	      else
1738	        {
1739		  breaktype = n;
1740	          Msg(0, "breaktype set to (%d) %s", n, types[n]);
1741		}
1742	    }
1743	  else
1744	    Msg(0, "breaktype is (%d) %s", breaktype, types[breaktype]);
1745	}
1746      break;
1747    case RC_POW_BREAK:
1748    case RC_BREAK:
1749      n = 0;
1750      if (*args && ParseNum(act, &n))
1751	break;
1752      SendBreak(fore, n, nr == RC_POW_BREAK);
1753      break;
1754#ifdef LOCK
1755    case RC_LOCKSCREEN:
1756      Detach(D_LOCK);
1757      break;
1758#endif
1759    case RC_WIDTH:
1760    case RC_HEIGHT:
1761      {
1762	int w, h;
1763	int what = 0;
1764
1765        i = 1;
1766	if (*args && !strcmp(*args, "-w"))
1767	  what = 1;
1768	else if (*args && !strcmp(*args, "-d"))
1769	  what = 2;
1770	if (what)
1771	  args++;
1772	if (what == 0 && flayer && !display)
1773	  what = 1;
1774	if (what == 1)
1775	  {
1776	    if (!flayer)
1777	      {
1778		Msg(0, "%s: %s: window required", rc_name, comms[nr].name);
1779		break;
1780	      }
1781	    w = flayer->l_width;
1782	    h = flayer->l_height;
1783	  }
1784	else
1785	  {
1786	    if (!display)
1787	      {
1788		Msg(0, "%s: %s: display required", rc_name, comms[nr].name);
1789		break;
1790	      }
1791	    w = D_width;
1792	    h = D_height;
1793	  }
1794        if (*args && args[0][0] == '-')
1795	  {
1796	    Msg(0, "%s: %s: unknown option %s", rc_name, comms[nr].name, *args);
1797	    break;
1798	  }
1799	if (nr == RC_HEIGHT)
1800	  {
1801	    if (!*args)
1802	      {
1803#define H0height 42
1804#define H1height 24
1805		if (h == H0height)
1806		  h = H1height;
1807		else if (h == H1height)
1808		  h = H0height;
1809		else if (h > (H0height + H1height) / 2)
1810		  h = H0height;
1811		else
1812		  h = H1height;
1813	      }
1814	    else
1815	      {
1816		h = atoi(*args);
1817		if (args[1])
1818		  w = atoi(args[1]);
1819	      }
1820	  }
1821	else
1822	  {
1823	    if (!*args)
1824	      {
1825		if (w == Z0width)
1826		  w = Z1width;
1827		else if (w == Z1width)
1828		  w = Z0width;
1829		else if (w > (Z0width + Z1width) / 2)
1830		  w = Z0width;
1831		else
1832		  w = Z1width;
1833	      }
1834	    else
1835	      {
1836		w = atoi(*args);
1837		if (args[1])
1838		  h = atoi(args[1]);
1839	      }
1840	  }
1841        if (*args && args[1] && args[2])
1842	  {
1843	    Msg(0, "%s: %s: too many arguments", rc_name, comms[nr].name);
1844	    break;
1845	  }
1846	if (w <= 0)
1847	  {
1848	    Msg(0, "Illegal width");
1849	    break;
1850	  }
1851	if (h <= 0)
1852	  {
1853	    Msg(0, "Illegal height");
1854	    break;
1855	  }
1856	if (what == 1)
1857	  {
1858	    if (flayer->l_width == w && flayer->l_height == h)
1859	      break;
1860	    ResizeLayer(flayer, w, h, (struct display *)0);
1861	    break;
1862	  }
1863	if (D_width == w && D_height == h)
1864	  break;
1865	if (what == 2)
1866	  {
1867	    ChangeScreenSize(w, h, 1);
1868	  }
1869	else
1870	  {
1871	    if (ResizeDisplay(w, h) == 0)
1872	      {
1873		Activate(D_fore ? D_fore->w_norefresh : 0);
1874		/* autofit */
1875		ResizeLayer(D_forecv->c_layer, D_forecv->c_xe - D_forecv->c_xs + 1, D_forecv->c_ye - D_forecv->c_ys + 1, 0);
1876		break;
1877	      }
1878	    if (h == D_height)
1879	      Msg(0, "Your termcap does not specify how to change the terminal's width to %d.", w);
1880	    else if (w == D_width)
1881	      Msg(0, "Your termcap does not specify how to change the terminal's height to %d.", h);
1882	    else
1883	      Msg(0, "Your termcap does not specify how to change the terminal's resolution to %dx%d.", w, h);
1884	  }
1885      }
1886      break;
1887    case RC_TITLE:
1888      if (*args == 0)
1889	InputAKA();
1890      else
1891	ChangeAKA(fore, *args, strlen(*args));
1892      break;
1893    case RC_COLON:
1894      Input(":", 100, INP_COOKED, Colonfin, NULL);
1895      if (*args && **args)
1896	{
1897	  s = *args;
1898	  n = strlen(s);
1899	  LayProcess(&s, &n);
1900	}
1901      break;
1902    case RC_LASTMSG:
1903      if (D_status_lastmsg)
1904	Msg(0, "%s", D_status_lastmsg);
1905      break;
1906    case RC_SCREEN:
1907      DoScreen("key", args);
1908      break;
1909    case RC_WRAP:
1910      if (ParseSwitch(act, &fore->w_wrap) == 0 && msgok)
1911        Msg(0, "%cwrap", fore->w_wrap ? '+' : '-');
1912      break;
1913    case RC_FLOW:
1914      if (*args)
1915	{
1916	  if (args[0][0] == 'a')
1917	    {
1918	      fore->w_flow = (fore->w_flow & FLOW_AUTO) ? FLOW_AUTOFLAG |FLOW_AUTO|FLOW_NOW : FLOW_AUTOFLAG;
1919	    }
1920	  else
1921	    {
1922	      if (ParseOnOff(act, &n))
1923		break;
1924	      fore->w_flow = (fore->w_flow & FLOW_AUTO) | n;
1925	    }
1926	}
1927      else
1928	{
1929	  if (fore->w_flow & FLOW_AUTOFLAG)
1930	    fore->w_flow = (fore->w_flow & FLOW_AUTO) | FLOW_NOW;
1931	  else if (fore->w_flow & FLOW_NOW)
1932	    fore->w_flow &= ~FLOW_NOW;
1933	  else
1934	    fore->w_flow = fore->w_flow ? FLOW_AUTOFLAG|FLOW_AUTO|FLOW_NOW : FLOW_AUTOFLAG;
1935	}
1936      SetFlow(fore->w_flow & FLOW_NOW);
1937      if (msgok)
1938	Msg(0, "%cflow%s", (fore->w_flow & FLOW_NOW) ? '+' : '-',
1939	    (fore->w_flow & FLOW_AUTOFLAG) ? "(auto)" : "");
1940      break;
1941#ifdef MULTIUSER
1942    case RC_DEFWRITELOCK:
1943      if (args[0][0] == 'a')
1944	nwin_default.wlock = WLOCK_AUTO;
1945      else
1946	{
1947	  if (ParseOnOff(act, &n))
1948	    break;
1949	  nwin_default.wlock = n ? WLOCK_ON : WLOCK_OFF;
1950	}
1951      break;
1952    case RC_WRITELOCK:
1953      if (*args)
1954	{
1955	  if (args[0][0] == 'a')
1956	    {
1957	      fore->w_wlock = WLOCK_AUTO;
1958	    }
1959	  else
1960	    {
1961	      if (ParseOnOff(act, &n))
1962		break;
1963	      fore->w_wlock = n ? WLOCK_ON : WLOCK_OFF;
1964	    }
1965	  /*
1966	   * user may have permission to change the writelock setting,
1967	   * but he may never aquire the lock himself without write permission
1968	   */
1969	  if (!AclCheckPermWin(D_user, ACL_WRITE, fore))
1970	    fore->w_wlockuser = D_user;
1971	}
1972      Msg(0, "writelock %s", (fore->w_wlock == WLOCK_AUTO) ? "auto" :
1973	  ((fore->w_wlock == WLOCK_OFF) ? "off" : "on"));
1974      break;
1975#endif
1976    case RC_CLEAR:
1977      ResetAnsiState(fore);
1978      WriteString(fore, "\033[H\033[J", 6);
1979      break;
1980    case RC_RESET:
1981      ResetAnsiState(fore);
1982#ifdef ZMODEM
1983      if (fore->w_zdisplay)
1984        zmodem_abort(fore, fore->w_zdisplay);
1985#endif
1986      WriteString(fore, "\033c", 2);
1987      break;
1988    case RC_MONITOR:
1989      n = fore->w_monitor != MON_OFF;
1990      if (ParseSwitch(act, &n))
1991	break;
1992      if (n)
1993	{
1994#ifdef MULTIUSER
1995	  if (display)	/* we tell only this user */
1996	    ACLBYTE(fore->w_mon_notify, D_user->u_id) |= ACLBIT(D_user->u_id);
1997	  else
1998	    for (i = 0; i < maxusercount; i++)
1999	      ACLBYTE(fore->w_mon_notify, i) |= ACLBIT(i);
2000#endif
2001	  if (fore->w_monitor == MON_OFF)
2002	    fore->w_monitor = MON_ON;
2003	  Msg(0, "Window %d (%s) is now being monitored for all activity.", fore->w_number, fore->w_title);
2004	}
2005      else
2006	{
2007#ifdef MULTIUSER
2008	  if (display) /* we remove only this user */
2009	    ACLBYTE(fore->w_mon_notify, D_user->u_id)
2010	      &= ~ACLBIT(D_user->u_id);
2011	  else
2012	    for (i = 0; i < maxusercount; i++)
2013	      ACLBYTE(fore->w_mon_notify, i) &= ~ACLBIT(i);
2014	  for (i = maxusercount - 1; i >= 0; i--)
2015	    if (ACLBYTE(fore->w_mon_notify, i))
2016	      break;
2017	  if (i < 0)
2018#endif
2019	    fore->w_monitor = MON_OFF;
2020	  Msg(0, "Window %d (%s) is no longer being monitored for activity.", fore->w_number, fore->w_title);
2021	}
2022      break;
2023#ifdef MULTI
2024    case RC_DISPLAYS:
2025      display_displays();
2026      break;
2027#endif
2028    case RC_WINDOWLIST:
2029      if (!*args)
2030        display_wlist(0, WLIST_NUM);
2031      else if (!strcmp(*args, "-m") && !args[1])
2032        display_wlist(0, WLIST_MRU);
2033      else if (!strcmp(*args, "-b") && !args[1])
2034        display_wlist(1, WLIST_NUM);
2035      else if (!strcmp(*args, "-b") && !strcmp(args[1], "-m") && !args[2])
2036        display_wlist(1, WLIST_MRU);
2037      else if (!strcmp(*args, "-m") && !strcmp(args[1], "-b") && !args[2])
2038        display_wlist(1, WLIST_MRU);
2039      else if (!strcmp(*args, "string"))
2040	{
2041	  if (args[1])
2042	    {
2043	      if (wliststr)
2044		free(wliststr);
2045	      wliststr = SaveStr(args[1]);
2046	    }
2047	  if (msgok)
2048	    Msg(0, "windowlist string is '%s'", wliststr);
2049	}
2050      else if (!strcmp(*args, "title"))
2051	{
2052	  if (args[1])
2053	    {
2054	      if (wlisttit)
2055		free(wlisttit);
2056	      wlisttit = SaveStr(args[1]);
2057	    }
2058	  if (msgok)
2059	    Msg(0, "windowlist title is '%s'", wlisttit);
2060	}
2061      else
2062	Msg(0, "usage: windowlist [-b] [string [string] | title [title]]");
2063      break;
2064    case RC_HELP:
2065      if (argc == 2 && !strcmp(*args, "-c"))
2066	{
2067	  struct action *ktabp;
2068	  if ((ktabp = FindKtab(args[1], 0)) == 0)
2069	    {
2070	      Msg(0, "Unknown command class '%s'", args[1]);
2071	      break;
2072	    }
2073          display_help(args[1], ktabp);
2074	}
2075      else
2076        display_help((char *)0, ktab);
2077      break;
2078    case RC_LICENSE:
2079      display_copyright();
2080      break;
2081#ifdef COPY_PASTE
2082    case RC_COPY:
2083      if (flayer->l_layfn != &WinLf)
2084	{
2085	  Msg(0, "Must be on a window layer");
2086	  break;
2087	}
2088      MarkRoutine();
2089      break;
2090    case RC_HISTORY:
2091      {
2092        static char *pasteargs[] = {".", 0};
2093	static int pasteargl[] = {1};
2094
2095	if (flayer->l_layfn != &WinLf)
2096	  {
2097	    Msg(0, "Must be on a window layer");
2098	    break;
2099	  }
2100	if (GetHistory() == 0)
2101	  break;
2102	if (user->u_plop.buf == NULL)
2103	  break;
2104	args = pasteargs;
2105	argl = pasteargl;
2106      }
2107      /*FALLTHROUGH*/
2108    case RC_PASTE:
2109      {
2110        char *ss, *dbuf, dch;
2111        int l = 0;
2112# ifdef ENCODINGS
2113	int enc = -1;
2114# endif
2115
2116	/*
2117	 * without args we prompt for one(!) register to be pasted in the window
2118	 */
2119	if ((s = *args) == NULL)
2120	  {
2121	    Input("Paste from register:", 1, INP_RAW, ins_reg_fn, NULL);
2122	    break;
2123	  }
2124	if (args[1] == 0 && !fore)	/* no window? */
2125	  break;
2126	/*
2127	 * with two arguments we paste into a destination register
2128	 * (no window needed here).
2129	 */
2130	if (args[1] && argl[1] != 1)
2131	  {
2132	    Msg(0, "%s: paste destination: character, ^x, or (octal) \\032 expected.",
2133		rc_name);
2134	    break;
2135	  }
2136# ifdef ENCODINGS
2137        else if (fore)
2138	  enc = fore->w_encoding;
2139# endif
2140
2141	/*
2142	 * measure length of needed buffer
2143	 */
2144        for (ss = s = *args; (ch = *ss); ss++)
2145          {
2146	    if (ch == '.')
2147	      {
2148# ifdef ENCODINGS
2149		if (enc == -1)
2150		  enc = user->u_plop.enc;
2151		if (enc != user->u_plop.enc)
2152		  l += RecodeBuf((unsigned char *)user->u_plop.buf, user->u_plop.len, user->u_plop.enc, enc, (unsigned char *)0);
2153		else
2154# endif
2155		  l += user->u_plop.len;
2156	      }
2157	    else
2158	      {
2159# ifdef ENCODINGS
2160		if (enc == -1)
2161		  enc = plop_tab[(int)(unsigned char)ch].enc;
2162		if (enc != plop_tab[(int)(unsigned char)ch].enc)
2163		  l += RecodeBuf((unsigned char *)plop_tab[(int)(unsigned char)ch].buf, plop_tab[(int)(unsigned char)ch].len, plop_tab[(int)(unsigned char)ch].enc, enc, (unsigned char *)0);
2164		else
2165# endif
2166                  l += plop_tab[(int)(unsigned char)ch].len;
2167	      }
2168          }
2169        if (l == 0)
2170	  {
2171	    Msg(0, "empty buffer");
2172	    break;
2173	  }
2174	/*
2175	 * shortcut:
2176	 * if there is only one source and the destination is a window, then
2177	 * pass a pointer rather than duplicating the buffer.
2178	 */
2179        if (s[1] == 0 && args[1] == 0)
2180# ifdef ENCODINGS
2181	  if (enc == (*s == '.' ? user->u_plop.enc : plop_tab[(int)(unsigned char)*s].enc))
2182# endif
2183            {
2184	      MakePaster(&fore->w_paster, *s == '.' ? user->u_plop.buf : plop_tab[(int)(unsigned char)*s].buf, l, 0);
2185	      break;
2186            }
2187	/*
2188	 * if no shortcut, we construct a buffer
2189	 */
2190        if ((dbuf = (char *)malloc(l)) == 0)
2191          {
2192	    Msg(0, strnomem);
2193	    break;
2194          }
2195        l = 0;
2196	/*
2197	 * concatenate all sources into our own buffer, copy buffer is
2198	 * special and is skipped if no display exists.
2199	 */
2200        for (ss = s; (ch = *ss); ss++)
2201          {
2202	    struct plop *pp = (ch == '.' ? &user->u_plop : &plop_tab[(int)(unsigned char)ch]);
2203#ifdef ENCODINGS
2204	    if (pp->enc != enc)
2205	      {
2206		l += RecodeBuf((unsigned char *)pp->buf, pp->len, pp->enc, enc, (unsigned char *)dbuf + l);
2207		continue;
2208	      }
2209#endif
2210	    bcopy(pp->buf, dbuf + l, pp->len);
2211	    l += pp->len;
2212          }
2213	/*
2214	 * when called with one argument we paste our buffer into the window
2215	 */
2216	if (args[1] == 0)
2217	  {
2218	    MakePaster(&fore->w_paster, dbuf, l, 1);
2219	  }
2220	else
2221	  {
2222	    /*
2223	     * we have two arguments, the second is already in dch.
2224	     * use this as destination rather than the window.
2225	     */
2226	    dch = args[1][0];
2227	    if (dch == '.')
2228	      {
2229	        if (user->u_plop.buf != NULL)
2230	          UserFreeCopyBuffer(user);
2231		user->u_plop.buf = dbuf;
2232		user->u_plop.len = l;
2233#ifdef ENCODINGS
2234		user->u_plop.enc = enc;
2235#endif
2236	      }
2237	    else
2238	      {
2239		struct plop *pp = plop_tab + (int)(unsigned char)dch;
2240		if (pp->buf)
2241		  free(pp->buf);
2242		pp->buf = dbuf;
2243		pp->len = l;
2244#ifdef ENCODINGS
2245		pp->enc = enc;
2246#endif
2247	      }
2248	  }
2249        break;
2250      }
2251    case RC_WRITEBUF:
2252      if (!user->u_plop.buf)
2253	{
2254	  Msg(0, "empty buffer");
2255	  break;
2256	}
2257#ifdef ENCODINGS
2258	{
2259	  struct plop oldplop;
2260
2261	  oldplop = user->u_plop;
2262	  if (args[0] && args[1] && !strcmp(args[0], "-e"))
2263	    {
2264	      int enc, l;
2265	      char *newbuf;
2266
2267	      enc = FindEncoding(args[1]);
2268	      if (enc == -1)
2269		{
2270		  Msg(0, "%s: writebuf: unknown encoding", rc_name);
2271		  break;
2272		}
2273	      if (enc != oldplop.enc)
2274		{
2275		  l = RecodeBuf((unsigned char *)oldplop.buf, oldplop.len, oldplop.enc, enc, (unsigned char *)0);
2276		  newbuf = malloc(l + 1);
2277		  if (!newbuf)
2278		    {
2279		      Msg(0, strnomem);
2280		      break;
2281		    }
2282		  user->u_plop.len = RecodeBuf((unsigned char *)oldplop.buf, oldplop.len, oldplop.enc, enc, (unsigned char *)newbuf);
2283		  user->u_plop.buf = newbuf;
2284		  user->u_plop.enc = enc;
2285		}
2286	      args += 2;
2287	    }
2288#endif
2289	  if (args[0] && args[1])
2290	    Msg(0, "%s: writebuf: too many arguments", rc_name);
2291	  else
2292	    WriteFile(user, args[0], DUMP_EXCHANGE);
2293#ifdef ENCODINGS
2294	  if (user->u_plop.buf != oldplop.buf)
2295	    free(user->u_plop.buf);
2296	  user->u_plop = oldplop;
2297	}
2298#endif
2299      break;
2300    case RC_READBUF:
2301#ifdef ENCODINGS
2302      i = fore ? fore->w_encoding : display ? display->d_encoding : 0;
2303      if (args[0] && args[1] && !strcmp(args[0], "-e"))
2304	{
2305	  i = FindEncoding(args[1]);
2306	  if (i == -1)
2307	    {
2308	      Msg(0, "%s: readbuf: unknown encoding", rc_name);
2309	      break;
2310	    }
2311	  args += 2;
2312	}
2313#endif
2314      if (args[0] && args[1])
2315	{
2316	  Msg(0, "%s: readbuf: too many arguments", rc_name);
2317	  break;
2318	}
2319      if ((s = ReadFile(args[0] ? args[0] : BufferFile, &n)))
2320	{
2321	  if (user->u_plop.buf)
2322	    UserFreeCopyBuffer(user);
2323	  user->u_plop.len = n;
2324	  user->u_plop.buf = s;
2325#ifdef ENCODINGS
2326	  user->u_plop.enc = i;
2327#endif
2328	}
2329      break;
2330    case RC_REMOVEBUF:
2331      KillBuffers();
2332      break;
2333    case RC_IGNORECASE:
2334      (void)ParseSwitch(act, &search_ic);
2335      if (msgok)
2336        Msg(0, "Will %signore case in searches", search_ic ? "" : "not ");
2337      break;
2338#endif				/* COPY_PASTE */
2339    case RC_ESCAPE:
2340      if (*argl == 0)
2341	SetEscape(user, -1, -1);
2342      else if (*argl == 2)
2343	SetEscape(user, (int)(unsigned char)args[0][0], (int)(unsigned char)args[0][1]);
2344      else
2345	{
2346	  Msg(0, "%s: two characters required after escape.", rc_name);
2347	  break;
2348	}
2349      /* Change defescape if master user. This is because we only
2350       * have one ktab.
2351       */
2352      if (display && user != users)
2353	break;
2354      /* FALLTHROUGH */
2355    case RC_DEFESCAPE:
2356      if (*argl == 0)
2357	SetEscape(NULL, -1, -1);
2358      else if (*argl == 2)
2359	SetEscape(NULL, (int)(unsigned char)args[0][0], (int)(unsigned char)args[0][1]);
2360      else
2361	{
2362	  Msg(0, "%s: two characters required after defescape.", rc_name);
2363	  break;
2364	}
2365#ifdef MAPKEYS
2366      CheckEscape();
2367#endif
2368      break;
2369    case RC_CHDIR:
2370      s = *args ? *args : home;
2371      if (chdir(s) == -1)
2372	Msg(errno, "%s", s);
2373      break;
2374    case RC_SHELL:
2375    case RC_DEFSHELL:
2376      if (ParseSaveStr(act, &ShellProg) == 0)
2377        ShellArgs[0] = ShellProg;
2378      break;
2379    case RC_HARDCOPYDIR:
2380      if (*args)
2381        (void)ParseSaveStr(act, &hardcopydir);
2382      if (msgok)
2383	Msg(0, "hardcopydir is %s\n", hardcopydir && *hardcopydir ? hardcopydir : "<cwd>");
2384      break;
2385    case RC_LOGFILE:
2386      if (*args)
2387	{
2388	  if (args[1] && !(strcmp(*args, "flush")))
2389	    {
2390	      log_flush = atoi(args[1]);
2391	      if (msgok)
2392		Msg(0, "log flush timeout set to %ds\n", log_flush);
2393	      break;
2394	    }
2395	  if (ParseSaveStr(act, &screenlogfile) || !msgok)
2396	    break;
2397	}
2398      Msg(0, "logfile is '%s'", screenlogfile);
2399      break;
2400    case RC_LOGTSTAMP:
2401      if (!*args || !strcmp(*args, "on") || !strcmp(*args, "off"))
2402        {
2403	  if (ParseSwitch(act, &logtstamp_on) == 0 && msgok)
2404            Msg(0, "timestamps turned %s", logtstamp_on ? "on" : "off");
2405        }
2406      else if (!strcmp(*args, "string"))
2407	{
2408	  if (args[1])
2409	    {
2410	      if (logtstamp_string)
2411		free(logtstamp_string);
2412	      logtstamp_string = SaveStr(args[1]);
2413	    }
2414	  if (msgok)
2415	    Msg(0, "logfile timestamp is '%s'", logtstamp_string);
2416	}
2417      else if (!strcmp(*args, "after"))
2418	{
2419	  if (args[1])
2420	    {
2421	      logtstamp_after = atoi(args[1]);
2422	      if (!msgok)
2423		break;
2424	    }
2425	  Msg(0, "timestamp printed after %ds\n", logtstamp_after);
2426	}
2427      else
2428        Msg(0, "usage: logtstamp [after [n]|string [str]|on|off]");
2429      break;
2430    case RC_SHELLTITLE:
2431      (void)ParseSaveStr(act, &nwin_default.aka);
2432      break;
2433    case RC_TERMCAP:
2434    case RC_TERMCAPINFO:
2435    case RC_TERMINFO:
2436      if (!rc_name || !*rc_name)
2437        Msg(0, "Sorry, too late now. Place that in your .screenrc file.");
2438      break;
2439    case RC_SLEEP:
2440      break;			/* Already handled */
2441    case RC_TERM:
2442      s = NULL;
2443      if (ParseSaveStr(act, &s))
2444	break;
2445      if (strlen(s) >= 20)
2446	{
2447	  Msg(0, "%s: term: argument too long ( < 20)", rc_name);
2448	  free(s);
2449	  break;
2450	}
2451      strcpy(screenterm, s);
2452      free(s);
2453      debug1("screenterm set to %s\n", screenterm);
2454      MakeTermcap((display == 0));
2455      debug("new termcap made\n");
2456      break;
2457    case RC_ECHO:
2458      if (!msgok && (!rc_name || strcmp(rc_name, "-X")))
2459	break;
2460      /*
2461       * user typed ^A:echo... well, echo isn't FinishRc's job,
2462       * but as he wanted to test us, we show good will
2463       */
2464      if (argc > 1 && !strcmp(*args, "-n"))
2465	{
2466	  args++;
2467	  argc--;
2468	}
2469      s = *args;
2470      if (argc > 1 && !strcmp(*args, "-p"))
2471	{
2472	  args++;
2473	  argc--;
2474	  s = *args;
2475	  if (s)
2476	    s = MakeWinMsg(s, fore, '%');
2477	}
2478      if (s)
2479	Msg(0, "%s", s);
2480      else
2481	Msg(0, "%s: 'echo [-n] [-p] \"string\"' expected.", rc_name);
2482      break;
2483    case RC_BELL:
2484    case RC_BELL_MSG:
2485      if (*args == 0)
2486	{
2487	  char buf[256];
2488	  AddXChars(buf, sizeof(buf), BellString);
2489	  Msg(0, "bell_msg is '%s'", buf);
2490	  break;
2491	}
2492      (void)ParseSaveStr(act, &BellString);
2493      break;
2494#ifdef COPY_PASTE
2495    case RC_BUFFERFILE:
2496      if (*args == 0)
2497	BufferFile = SaveStr(DEFAULT_BUFFERFILE);
2498      else if (ParseSaveStr(act, &BufferFile))
2499        break;
2500      if (msgok)
2501        Msg(0, "Bufferfile is now '%s'", BufferFile);
2502      break;
2503#endif
2504    case RC_ACTIVITY:
2505      (void)ParseSaveStr(act, &ActivityString);
2506      break;
2507#if defined(DETACH) && defined(POW_DETACH)
2508    case RC_POW_DETACH_MSG:
2509      if (*args == 0)
2510        {
2511	  char buf[256];
2512          AddXChars(buf, sizeof(buf), PowDetachString);
2513	  Msg(0, "pow_detach_msg is '%s'", buf);
2514	  break;
2515	}
2516      (void)ParseSaveStr(act, &PowDetachString);
2517      break;
2518#endif
2519#if defined(UTMPOK) && defined(LOGOUTOK)
2520    case RC_LOGIN:
2521      n = fore->w_slot != (slot_t)-1;
2522      if (*args && !strcmp(*args, "always"))
2523	{
2524	  fore->w_lflag = 3;
2525	  if (!displays && n)
2526	    SlotToggle(n);
2527	  break;
2528	}
2529      if (*args && !strcmp(*args, "attached"))
2530	{
2531	  fore->w_lflag = 1;
2532	  if (!displays && n)
2533	    SlotToggle(0);
2534	  break;
2535	}
2536      if (ParseSwitch(act, &n) == 0)
2537        SlotToggle(n);
2538      break;
2539    case RC_DEFLOGIN:
2540      if (!strcmp(*args, "always"))
2541	nwin_default.lflag |= 2;
2542      else if (!strcmp(*args, "attached"))
2543	nwin_default.lflag &= ~2;
2544      else
2545        (void)ParseOnOff(act, &nwin_default.lflag);
2546      break;
2547#endif
2548    case RC_DEFFLOW:
2549      if (args[0] && args[1] && args[1][0] == 'i')
2550	{
2551	  iflag = 1;
2552	  for (display = displays; display; display = display->d_next)
2553	    {
2554	      if (!D_flow)
2555		continue;
2556#if defined(TERMIO) || defined(POSIX)
2557	      D_NewMode.tio.c_cc[VINTR] = D_OldMode.tio.c_cc[VINTR];
2558	      D_NewMode.tio.c_lflag |= ISIG;
2559#else /* TERMIO || POSIX */
2560	      D_NewMode.m_tchars.t_intrc = D_OldMode.m_tchars.t_intrc;
2561#endif /* TERMIO || POSIX */
2562	      SetTTY(D_userfd, &D_NewMode);
2563	    }
2564	}
2565      if (args[0] && args[0][0] == 'a')
2566	nwin_default.flowflag = FLOW_AUTOFLAG;
2567      else
2568	(void)ParseOnOff(act, &nwin_default.flowflag);
2569      break;
2570    case RC_DEFWRAP:
2571      (void)ParseOnOff(act, &nwin_default.wrap);
2572      break;
2573    case RC_DEFC1:
2574      (void)ParseOnOff(act, &nwin_default.c1);
2575      break;
2576#ifdef COLOR
2577    case RC_DEFBCE:
2578      (void)ParseOnOff(act, &nwin_default.bce);
2579      break;
2580#endif
2581    case RC_DEFGR:
2582      (void)ParseOnOff(act, &nwin_default.gr);
2583      break;
2584    case RC_DEFMONITOR:
2585      if (ParseOnOff(act, &n) == 0)
2586        nwin_default.monitor = (n == 0) ? MON_OFF : MON_ON;
2587      break;
2588    case RC_DEFSILENCE:
2589      if (ParseOnOff(act, &n) == 0)
2590        nwin_default.silence = (n == 0) ? SILENCE_OFF : SILENCE_ON;
2591      break;
2592    case RC_VERBOSE:
2593      if (!*args)
2594	Msg(0, "W%s echo command when creating windows.",
2595	  VerboseCreate ? "ill" : "on't");
2596      else if (ParseOnOff(act, &n) == 0)
2597        VerboseCreate = n;
2598      break;
2599    case RC_HARDSTATUS:
2600      if (display)
2601	{
2602	  Msg(0, "%s", "");	/* wait till mintime (keep gcc quiet) */
2603          RemoveStatus();
2604	}
2605      if (args[0] && strcmp(args[0], "on") && strcmp(args[0], "off"))
2606	{
2607          struct display *olddisplay = display;
2608	  int old_use, new_use = -1;
2609
2610	  s = args[0];
2611	  if (!strncmp(s, "always", 6))
2612	    s += 6;
2613	  if (!strcmp(s, "lastline"))
2614	    new_use = HSTATUS_LASTLINE;
2615	  else if (!strcmp(s, "ignore"))
2616	    new_use = HSTATUS_IGNORE;
2617	  else if (!strcmp(s, "message"))
2618	    new_use = HSTATUS_MESSAGE;
2619	  else if (!strcmp(args[0], "string"))
2620	    {
2621	      if (!args[1])
2622		{
2623		  char buf[256];
2624		  AddXChars(buf, sizeof(buf), hstatusstring);
2625		  Msg(0, "hardstatus string is '%s'", buf);
2626		  break;
2627		}
2628	    }
2629	  else
2630	    {
2631	      Msg(0, "%s: usage: hardstatus [always]lastline|ignore|message|string [string]", rc_name);
2632	      break;
2633	    }
2634	  if (new_use != -1)
2635	    {
2636	      hardstatusemu = new_use | (s == args[0] ? 0 : HSTATUS_ALWAYS);
2637	      for (display = displays; display; display = display->d_next)
2638		{
2639		  RemoveStatus();
2640		  new_use = hardstatusemu & ~HSTATUS_ALWAYS;
2641		  if (D_HS && s == args[0])
2642		    new_use = HSTATUS_HS;
2643		  ShowHStatus((char *)0);
2644		  old_use = D_has_hstatus;
2645		  D_has_hstatus = new_use;
2646		  if ((new_use == HSTATUS_LASTLINE && old_use != HSTATUS_LASTLINE) || (new_use != HSTATUS_LASTLINE && old_use == HSTATUS_LASTLINE))
2647		    ChangeScreenSize(D_width, D_height, 1);
2648		  RefreshHStatus();
2649		}
2650	    }
2651	  if (args[1])
2652	    {
2653	      if (hstatusstring)
2654		free(hstatusstring);
2655	      hstatusstring = SaveStr(args[1]);
2656	      for (display = displays; display; display = display->d_next)
2657	        RefreshHStatus();
2658	    }
2659	  display = olddisplay;
2660	  break;
2661	}
2662      (void)ParseSwitch(act, &use_hardstatus);
2663      if (msgok)
2664        Msg(0, "messages displayed on %s", use_hardstatus ? "hardstatus line" : "window");
2665      break;
2666    case RC_CAPTION:
2667      if (strcmp(args[0], "always") == 0 || strcmp(args[0], "splitonly") == 0)
2668	{
2669	  struct display *olddisplay = display;
2670
2671	  captionalways = args[0][0] == 'a';
2672	  for (display = displays; display; display = display->d_next)
2673	    ChangeScreenSize(D_width, D_height, 1);
2674	  display = olddisplay;
2675	}
2676      else if (strcmp(args[0], "string") == 0)
2677	{
2678	  if (!args[1])
2679	    {
2680	      char buf[256];
2681	      AddXChars(buf, sizeof(buf), captionstring);
2682	      Msg(0, "caption string is '%s'", buf);
2683	      break;
2684	    }
2685	}
2686      else
2687	{
2688	  Msg(0, "%s: usage: caption always|splitonly|string <string>", rc_name);
2689	  break;
2690	}
2691      if (!args[1])
2692	break;
2693      if (captionstring)
2694	free(captionstring);
2695      captionstring = SaveStr(args[1]);
2696      RedisplayDisplays(0);
2697      break;
2698    case RC_CONSOLE:
2699      n = (console_window != 0);
2700      if (ParseSwitch(act, &n))
2701        break;
2702      if (TtyGrabConsole(fore->w_ptyfd, n, rc_name))
2703	break;
2704      if (n == 0)
2705	  Msg(0, "%s: releasing console %s", rc_name, HostName);
2706      else if (console_window)
2707	  Msg(0, "%s: stealing console %s from window %d (%s)", rc_name,
2708	      HostName, console_window->w_number, console_window->w_title);
2709      else
2710	  Msg(0, "%s: grabbing console %s", rc_name, HostName);
2711      console_window = n ? fore : 0;
2712      break;
2713    case RC_ALLPARTIAL:
2714      if (ParseOnOff(act, &all_norefresh))
2715	break;
2716      if (!all_norefresh && fore)
2717	Activate(-1);
2718      if (msgok)
2719        Msg(0, all_norefresh ? "No refresh on window change!\n" :
2720			       "Window specific refresh\n");
2721      break;
2722    case RC_PARTIAL:
2723      (void)ParseSwitch(act, &n);
2724      fore->w_norefresh = n;
2725      break;
2726    case RC_VBELL:
2727      if (ParseSwitch(act, &visual_bell) || !msgok)
2728        break;
2729      if (visual_bell == 0)
2730        Msg(0, "switched to audible bell.");
2731      else
2732        Msg(0, "switched to visual bell.");
2733      break;
2734    case RC_VBELLWAIT:
2735      if (ParseNum1000(act, &VBellWait) == 0 && msgok)
2736        Msg(0, "vbellwait set to %.10g seconds", VBellWait/1000.);
2737      break;
2738    case RC_MSGWAIT:
2739      if (ParseNum1000(act, &MsgWait) == 0 && msgok)
2740        Msg(0, "msgwait set to %.10g seconds", MsgWait/1000.);
2741      break;
2742    case RC_MSGMINWAIT:
2743      if (ParseNum1000(act, &MsgMinWait) == 0 && msgok)
2744        Msg(0, "msgminwait set to %.10g seconds", MsgMinWait/1000.);
2745      break;
2746    case RC_SILENCEWAIT:
2747      if (ParseNum(act, &SilenceWait))
2748	break;
2749      if (SilenceWait < 1)
2750	SilenceWait = 1;
2751      for (p = windows; p; p = p->w_next)
2752	p->w_silencewait = SilenceWait;
2753      if (msgok)
2754	Msg(0, "silencewait set to %d seconds", SilenceWait);
2755      break;
2756    case RC_NUMBER:
2757      if (*args == 0)
2758        Msg(0, "This is window %d (%s).\n", fore->w_number, fore->w_title);
2759      else
2760        {
2761	  int old = fore->w_number;
2762
2763	  if (ParseNum(act, &n) || n >= maxwin)
2764	    break;
2765	  p = wtab[n];
2766	  wtab[n] = fore;
2767	  fore->w_number = n;
2768	  wtab[old] = p;
2769	  if (p)
2770	    p->w_number = old;
2771#ifdef MULTIUSER
2772	  /* exchange the acls for these windows. */
2773	  AclWinSwap(old, n);
2774#endif
2775#ifdef UTMPOK
2776	  /* exchange the utmp-slots for these windows */
2777	  if ((fore->w_slot != (slot_t) -1) && (fore->w_slot != (slot_t) 0))
2778	    {
2779	      RemoveUtmp(fore);
2780	      SetUtmp(fore);
2781	    }
2782	  if (p && (p->w_slot != (slot_t) -1) && (p->w_slot != (slot_t) 0))
2783	    {
2784	      /* XXX: first display wins? */
2785	      display = fore->w_layer.l_cvlist ? fore->w_layer.l_cvlist->c_display : 0;
2786	      RemoveUtmp(p);
2787	      SetUtmp(p);
2788	    }
2789#endif
2790
2791	  WindowChanged(fore, 'n');
2792	  WindowChanged((struct win *)0, 'w');
2793	  WindowChanged((struct win *)0, 'W');
2794	  WindowChanged((struct win *)0, 0);
2795	}
2796      break;
2797    case RC_SILENCE:
2798      n = fore->w_silence != 0;
2799      i = fore->w_silencewait;
2800      if (args[0] && (args[0][0] == '-' || (args[0][0] >= '0' && args[0][0] <= '9')))
2801        {
2802	  if (ParseNum(act, &i))
2803	    break;
2804	  n = i > 0;
2805	}
2806      else if (ParseSwitch(act, &n))
2807        break;
2808      if (n)
2809        {
2810#ifdef MULTIUSER
2811	  if (display)	/* we tell only this user */
2812	    ACLBYTE(fore->w_lio_notify, D_user->u_id) |= ACLBIT(D_user->u_id);
2813	  else
2814	    for (n = 0; n < maxusercount; n++)
2815	      ACLBYTE(fore->w_lio_notify, n) |= ACLBIT(n);
2816#endif
2817	  fore->w_silencewait = i;
2818	  fore->w_silence = SILENCE_ON;
2819	  SetTimeout(&fore->w_silenceev, fore->w_silencewait * 1000);
2820	  evenq(&fore->w_silenceev);
2821
2822	  if (!msgok)
2823	    break;
2824	  Msg(0, "The window is now being monitored for %d sec. silence.", fore->w_silencewait);
2825	}
2826      else
2827        {
2828#ifdef MULTIUSER
2829	  if (display) /* we remove only this user */
2830	    ACLBYTE(fore->w_lio_notify, D_user->u_id)
2831	      &= ~ACLBIT(D_user->u_id);
2832	  else
2833	    for (n = 0; n < maxusercount; n++)
2834	      ACLBYTE(fore->w_lio_notify, n) &= ~ACLBIT(n);
2835	  for (i = maxusercount - 1; i >= 0; i--)
2836	    if (ACLBYTE(fore->w_lio_notify, i))
2837	      break;
2838	  if (i < 0)
2839#endif
2840	    {
2841	      fore->w_silence = SILENCE_OFF;
2842	      evdeq(&fore->w_silenceev);
2843	    }
2844	  if (!msgok)
2845	    break;
2846	  Msg(0, "The window is no longer being monitored for silence.");
2847	}
2848      break;
2849#ifdef COPY_PASTE
2850    case RC_DEFSCROLLBACK:
2851      (void)ParseNum(act, &nwin_default.histheight);
2852      break;
2853    case RC_SCROLLBACK:
2854      (void)ParseNum(act, &n);
2855      ChangeWindowSize(fore, fore->w_width, fore->w_height, n);
2856      if (msgok)
2857	Msg(0, "scrollback set to %d", fore->w_histheight);
2858      break;
2859#endif
2860    case RC_SESSIONNAME:
2861      if (*args == 0)
2862	Msg(0, "This session is named '%s'\n", SockName);
2863      else
2864	{
2865	  char buf[MAXPATHLEN];
2866
2867	  s = 0;
2868	  if (ParseSaveStr(act, &s))
2869	    break;
2870	  if (!*s || strlen(s) + (SockName - SockPath) > MAXPATHLEN - 13 || index(s, '/'))
2871	    {
2872	      Msg(0, "%s: bad session name '%s'\n", rc_name, s);
2873	      free(s);
2874	      break;
2875	    }
2876	  strncpy(buf, SockPath, SockName - SockPath);
2877	  sprintf(buf + (SockName - SockPath), "%d.%s", (int)getpid(), s);
2878	  free(s);
2879	  if ((access(buf, F_OK) == 0) || (errno != ENOENT))
2880	    {
2881	      Msg(0, "%s: inappropriate path: '%s'.", rc_name, buf);
2882	      break;
2883	    }
2884	  if (rename(SockPath, buf))
2885	    {
2886	      Msg(errno, "%s: failed to rename(%s, %s)", rc_name, SockPath, buf);
2887	      break;
2888	    }
2889	  debug2("rename(%s, %s) done\n", SockPath, buf);
2890	  strcpy(SockPath, buf);
2891	  MakeNewEnv();
2892	}
2893      break;
2894    case RC_SETENV:
2895      if (!args[0] || !args[1])
2896        {
2897	  debug1("RC_SETENV arguments missing: %s\n", args[0] ? args[0] : "");
2898          InputSetenv(args[0]);
2899	}
2900      else
2901        {
2902          xsetenv(args[0], args[1]);
2903          MakeNewEnv();
2904	}
2905      break;
2906    case RC_UNSETENV:
2907      unsetenv(*args);
2908      MakeNewEnv();
2909      break;
2910#ifdef COPY_PASTE
2911    case RC_DEFSLOWPASTE:
2912      (void)ParseNum(act, &nwin_default.slow);
2913      break;
2914    case RC_SLOWPASTE:
2915      if (*args == 0)
2916	Msg(0, fore->w_slowpaste ?
2917               "Slowpaste in window %d is %d milliseconds." :
2918               "Slowpaste in window %d is unset.",
2919	    fore->w_number, fore->w_slowpaste);
2920      else if (ParseNum(act, &fore->w_slowpaste) == 0 && msgok)
2921	Msg(0, fore->w_slowpaste ?
2922               "Slowpaste in window %d set to %d milliseconds." :
2923               "Slowpaste in window %d now unset.",
2924	    fore->w_number, fore->w_slowpaste);
2925      break;
2926    case RC_MARKKEYS:
2927      if (CompileKeys(*args, *argl, mark_key_tab))
2928	{
2929	  Msg(0, "%s: markkeys: syntax error.", rc_name);
2930	  break;
2931	}
2932      debug1("markkeys %s\n", *args);
2933      break;
2934# ifdef FONT
2935    case RC_PASTEFONT:
2936      if (ParseSwitch(act, &pastefont) == 0 && msgok)
2937        Msg(0, "Will %spaste font settings", pastefont ? "" : "not ");
2938      break;
2939# endif
2940    case RC_CRLF:
2941      (void)ParseSwitch(act, &join_with_cr);
2942      break;
2943    case RC_COMPACTHIST:
2944      if (ParseSwitch(act, &compacthist) == 0 && msgok)
2945	Msg(0, "%scompacting history lines", compacthist ? "" : "not ");
2946      break;
2947#endif
2948#ifdef NETHACK
2949    case RC_NETHACK:
2950      (void)ParseOnOff(act, &nethackflag);
2951      break;
2952#endif
2953    case RC_HARDCOPY_APPEND:
2954      (void)ParseOnOff(act, &hardcopy_append);
2955      break;
2956    case RC_VBELL_MSG:
2957      if (*args == 0)
2958        {
2959	  char buf[256];
2960          AddXChars(buf, sizeof(buf), VisualBellString);
2961	  Msg(0, "vbell_msg is '%s'", buf);
2962	  break;
2963	}
2964      (void)ParseSaveStr(act, &VisualBellString);
2965      debug1(" new vbellstr '%s'\n", VisualBellString);
2966      break;
2967    case RC_DEFMODE:
2968      if (ParseBase(act, *args, &n, 8, "octal"))
2969        break;
2970      if (n < 0 || n > 0777)
2971	{
2972	  Msg(0, "%s: mode: Invalid tty mode %o", rc_name, n);
2973          break;
2974	}
2975      TtyMode = n;
2976      if (msgok)
2977	Msg(0, "Ttymode set to %03o", TtyMode);
2978      break;
2979    case RC_AUTODETACH:
2980      (void)ParseOnOff(act, &auto_detach);
2981      break;
2982    case RC_STARTUP_MESSAGE:
2983      (void)ParseOnOff(act, &default_startup);
2984      break;
2985#ifdef PASSWORD
2986    case RC_PASSWORD:
2987      if (*args)
2988	{
2989	  n = (*user->u_password) ? 1 : 0;
2990	  if (user->u_password != NullStr) free((char *)user->u_password);
2991	  user->u_password = SaveStr(*args);
2992	  if (!strcmp(user->u_password, "none"))
2993	    {
2994	      if (n)
2995	        Msg(0, "Password checking disabled");
2996	      free(user->u_password);
2997	      user->u_password = NullStr;
2998	    }
2999	}
3000      else
3001	{
3002	  if (!fore)
3003	    {
3004	      Msg(0, "%s: password: window required", rc_name);
3005	      break;
3006	    }
3007	  Input("New screen password:", 100, INP_NOECHO, pass1, display ? (char *)D_user : (char *)users);
3008	}
3009      break;
3010#endif				/* PASSWORD */
3011    case RC_BIND:
3012	{
3013	  struct action *ktabp = ktab;
3014
3015	  if (argc > 2 && !strcmp(*args, "-c"))
3016	    {
3017	      ktabp = FindKtab(args[1], 1);
3018	      if (ktabp == 0)
3019		break;
3020	      args += 2;
3021	      argl += 2;
3022	    }
3023	  if (*argl != 1)
3024	    {
3025	      Msg(0, "%s: bind: character, ^x, or (octal) \\032 expected.", rc_name);
3026	      break;
3027	    }
3028	  n = (unsigned char)args[0][0];
3029	  if (args[1])
3030	    {
3031	      if ((i = FindCommnr(args[1])) == RC_ILLEGAL)
3032		{
3033		  Msg(0, "%s: bind: unknown command '%s'", rc_name, args[1]);
3034		  break;
3035		}
3036	      if (CheckArgNum(i, args + 2) < 0)
3037		break;
3038	      ClearAction(&ktabp[n]);
3039	      SaveAction(ktabp + n, i, args + 2, argl + 2);
3040	    }
3041	  else
3042	    ClearAction(&ktabp[n]);
3043	}
3044      break;
3045#ifdef MAPKEYS
3046    case RC_BINDKEY:
3047	{
3048	  struct action *newact;
3049          int newnr, fl = 0, kf = 0, af = 0, df = 0, mf = 0;
3050	  struct display *odisp = display;
3051	  int used = 0;
3052          struct kmap_ext *kme;
3053
3054	  for (; *args && **args == '-'; args++, argl++)
3055	    {
3056	      if (strcmp(*args, "-t") == 0)
3057		fl = KMAP_NOTIMEOUT;
3058	      else if (strcmp(*args, "-k") == 0)
3059		kf = 1;
3060	      else if (strcmp(*args, "-a") == 0)
3061		af = 1;
3062	      else if (strcmp(*args, "-d") == 0)
3063		df = 1;
3064	      else if (strcmp(*args, "-m") == 0)
3065		mf = 1;
3066	      else if (strcmp(*args, "--") == 0)
3067		{
3068		  args++;
3069		  argl++;
3070		  break;
3071		}
3072	      else
3073		{
3074	          Msg(0, "%s: bindkey: invalid option %s", rc_name, *args);
3075		  return;
3076		}
3077	    }
3078	  if (df && mf)
3079	    {
3080	      Msg(0, "%s: bindkey: -d does not work with -m", rc_name);
3081	      break;
3082	    }
3083	  if (*args == 0)
3084	    {
3085	      if (mf)
3086		display_bindkey("Edit mode", mmtab);
3087	      else if (df)
3088		display_bindkey("Default", dmtab);
3089	      else
3090		display_bindkey("User", umtab);
3091	      break;
3092	    }
3093	  if (kf == 0)
3094	    {
3095	      if (af)
3096		{
3097		  Msg(0, "%s: bindkey: -a only works with -k", rc_name);
3098		  break;
3099		}
3100	      if (*argl == 0)
3101		{
3102		  Msg(0, "%s: bindkey: empty string makes no sense", rc_name);
3103		  break;
3104		}
3105	      for (i = 0, kme = kmap_exts; i < kmap_extn; i++, kme++)
3106		if (kme->str == 0)
3107		  {
3108		    if (args[1])
3109		      break;
3110		  }
3111		else
3112		  if (*argl == (kme->fl & ~KMAP_NOTIMEOUT) && bcmp(kme->str, *args, *argl) == 0)
3113		      break;
3114	      if (i == kmap_extn)
3115		{
3116		  if (!args[1])
3117		    {
3118		      Msg(0, "%s: bindkey: keybinding not found", rc_name);
3119		      break;
3120		    }
3121		  kmap_extn += 8;
3122		  kmap_exts = (struct kmap_ext *)xrealloc((char *)kmap_exts, kmap_extn * sizeof(*kmap_exts));
3123		  kme = kmap_exts + i;
3124		  bzero((char *)kme, 8 * sizeof(*kmap_exts));
3125		  for (; i < kmap_extn; i++, kme++)
3126		    {
3127		      kme->str = 0;
3128		      kme->dm.nr = kme->mm.nr = kme->um.nr = RC_ILLEGAL;
3129		      kme->dm.args = kme->mm.args = kme->um.args = noargs;
3130		    }
3131		  i -= 8;
3132		  kme -= 8;
3133		}
3134	      if (df == 0 && kme->dm.nr != RC_ILLEGAL)
3135		used = 1;
3136	      if (mf == 0 && kme->mm.nr != RC_ILLEGAL)
3137		used = 1;
3138	      if ((df || mf) && kme->um.nr != RC_ILLEGAL)
3139		used = 1;
3140	      i += KMAP_KEYS + KMAP_AKEYS;
3141	      newact = df ? &kme->dm : mf ? &kme->mm : &kme->um;
3142	    }
3143	  else
3144	    {
3145	      for (i = T_CAPS; i < T_OCAPS; i++)
3146		if (strcmp(term[i].tcname, *args) == 0)
3147		  break;
3148	      if (i == T_OCAPS)
3149		{
3150		  Msg(0, "%s: bindkey: unknown key '%s'", rc_name, *args);
3151		  break;
3152		}
3153	      if (af && i >= T_CURSOR && i < T_OCAPS)
3154	        i -=  T_CURSOR - KMAP_KEYS;
3155	      else
3156	        i -=  T_CAPS;
3157	      newact = df ? &dmtab[i] : mf ? &mmtab[i] : &umtab[i];
3158	    }
3159	  if (args[1])
3160	    {
3161	      if ((newnr = FindCommnr(args[1])) == RC_ILLEGAL)
3162		{
3163		  Msg(0, "%s: bindkey: unknown command '%s'", rc_name, args[1]);
3164		  break;
3165		}
3166	      if (CheckArgNum(newnr, args + 2) < 0)
3167		break;
3168	      ClearAction(newact);
3169	      SaveAction(newact, newnr, args + 2, argl + 2);
3170	      if (kf == 0 && args[1])
3171		{
3172		  if (kme->str)
3173		    free(kme->str);
3174		  kme->str = SaveStrn(*args, *argl);
3175		  kme->fl = fl | *argl;
3176		}
3177	    }
3178	  else
3179	    ClearAction(newact);
3180	  for (display = displays; display; display = display->d_next)
3181	    remap(i, args[1] ? 1 : 0);
3182	  if (kf == 0 && !args[1])
3183	    {
3184	      if (!used && kme->str)
3185		{
3186		  free(kme->str);
3187		  kme->str = 0;
3188		  kme->fl = 0;
3189		}
3190	    }
3191	  display = odisp;
3192	}
3193      break;
3194    case RC_MAPTIMEOUT:
3195      if (*args)
3196	{
3197          if (ParseNum(act, &n))
3198	    break;
3199	  if (n < 0)
3200	    {
3201	      Msg(0, "%s: maptimeout: illegal time %d", rc_name, n);
3202	      break;
3203	    }
3204	  maptimeout = n;
3205	}
3206      if (*args == 0 || msgok)
3207        Msg(0, "maptimeout is %dms", maptimeout);
3208      break;
3209    case RC_MAPNOTNEXT:
3210      D_dontmap = 1;
3211      break;
3212    case RC_MAPDEFAULT:
3213      D_mapdefault = 1;
3214      break;
3215#endif
3216#ifdef MULTIUSER
3217    case RC_ACLCHG:
3218    case RC_ACLADD:
3219    case RC_ADDACL:
3220    case RC_CHACL:
3221      UsersAcl(NULL, argc, args);
3222      break;
3223    case RC_ACLDEL:
3224      if (UserDel(args[0], NULL))
3225	break;
3226      if (msgok)
3227	Msg(0, "%s removed from acl database", args[0]);
3228      break;
3229    case RC_ACLGRP:
3230      /*
3231       * modify a user to gain or lose rights granted to a group.
3232       * This group is actually a normal user whose rights were defined
3233       * with chacl in the usual way.
3234       */
3235      if (args[1])
3236        {
3237	  if (strcmp(args[1], "none"))	/* link a user to another user */
3238	    {
3239	      if (AclLinkUser(args[0], args[1]))
3240		break;
3241	      if (msgok)
3242		Msg(0, "User %s joined acl-group %s", args[0], args[1]);
3243	    }
3244	  else				/* remove all groups from user */
3245	    {
3246	      struct acluser *u;
3247	      struct aclusergroup *g;
3248
3249	      if (!(u = *FindUserPtr(args[0])))
3250	        break;
3251	      while ((g = u->u_group))
3252	        {
3253		  u->u_group = g->next;
3254	  	  free((char *)g);
3255	        }
3256	    }
3257	}
3258      else				/* show all groups of user */
3259	{
3260	  char buf[256], *p = buf;
3261	  int ngroups = 0;
3262	  struct acluser *u;
3263	  struct aclusergroup *g;
3264
3265	  if (!(u = *FindUserPtr(args[0])))
3266	    {
3267	      if (msgok)
3268		Msg(0, "User %s does not exist.", args[0]);
3269	      break;
3270	    }
3271	  g = u->u_group;
3272	  while (g)
3273	    {
3274	      ngroups++;
3275	      sprintf(p, "%s ", g->u->u_name);
3276	      p += strlen(p);
3277	      if (p > buf+200)
3278		break;
3279	      g = g->next;
3280	    }
3281	  if (ngroups)
3282	    *(--p) = '\0';
3283	  Msg(0, "%s's group%s: %s.", args[0], (ngroups == 1) ? "" : "s",
3284	      (ngroups == 0) ? "none" : buf);
3285	}
3286      break;
3287    case RC_ACLUMASK:
3288    case RC_UMASK:
3289      while ((s = *args++))
3290        {
3291	  char *err = 0;
3292
3293	  if (AclUmask(display ? D_user : users, s, &err))
3294	    Msg(0, "umask: %s\n", err);
3295	}
3296      break;
3297    case RC_MULTIUSER:
3298      if (ParseOnOff(act, &n))
3299	break;
3300      multi = n ? "" : 0;
3301      chsock();
3302      if (msgok)
3303	Msg(0, "Multiuser mode %s", multi ? "enabled" : "disabled");
3304      break;
3305#endif /* MULTIUSER */
3306#ifdef PSEUDOS
3307    case RC_EXEC:
3308      winexec(args);
3309      break;
3310#endif
3311#ifdef MULTI
3312    case RC_NONBLOCK:
3313      i = D_nonblock >= 0;
3314      if (*args && ((args[0][0] >= '0' && args[0][0] <= '9') || args[0][0] == '.'))
3315	{
3316          if (ParseNum1000(act, &i))
3317	    break;
3318	}
3319      else if (!ParseSwitch(act, &i))
3320	i = i == 0 ? -1 : 1000;
3321      else
3322	break;
3323      if (msgok && i == -1)
3324        Msg(0, "display set to blocking mode");
3325      else if (msgok && i == 0)
3326        Msg(0, "display set to nonblocking mode, no timeout");
3327      else if (msgok)
3328        Msg(0, "display set to nonblocking mode, %.10gs timeout", i/1000.);
3329      D_nonblock = i;
3330      if (D_nonblock <= 0)
3331	evdeq(&D_blockedev);
3332      break;
3333    case RC_DEFNONBLOCK:
3334      if (*args && ((args[0][0] >= '0' && args[0][0] <= '9') || args[0][0] == '.'))
3335	{
3336          if (ParseNum1000(act, &defnonblock))
3337	    break;
3338	}
3339      else if (!ParseOnOff(act, &defnonblock))
3340        defnonblock = defnonblock == 0 ? -1 : 1000;
3341      else
3342	break;
3343      if (display && *rc_name)
3344	{
3345	  D_nonblock = defnonblock;
3346          if (D_nonblock <= 0)
3347	    evdeq(&D_blockedev);
3348	}
3349      break;
3350#endif
3351    case RC_GR:
3352#ifdef ENCODINGS
3353      if (fore->w_gr == 2)
3354	fore->w_gr = 0;
3355#endif
3356      if (ParseSwitch(act, &fore->w_gr) == 0 && msgok)
3357        Msg(0, "Will %suse GR", fore->w_gr ? "" : "not ");
3358#ifdef ENCODINGS
3359      if (fore->w_gr == 0 && fore->w_FontE)
3360	fore->w_gr = 2;
3361#endif
3362      break;
3363    case RC_C1:
3364      if (ParseSwitch(act, &fore->w_c1) == 0 && msgok)
3365        Msg(0, "Will %suse C1", fore->w_c1 ? "" : "not ");
3366      break;
3367#ifdef COLOR
3368    case RC_BCE:
3369      if (ParseSwitch(act, &fore->w_bce) == 0 && msgok)
3370        Msg(0, "Will %serase with background color", fore->w_bce ? "" : "not ");
3371      break;
3372#endif
3373#ifdef ENCODINGS
3374    case RC_KANJI:
3375    case RC_ENCODING:
3376#ifdef UTF8
3377      if (*args && !strcmp(args[0], "-d"))
3378	{
3379	  if (!args[1])
3380	    Msg(0, "encodings directory is %s", screenencodings ? screenencodings : "<unset>");
3381	  else
3382	    {
3383	      free(screenencodings);
3384	      screenencodings = SaveStr(args[1]);
3385	    }
3386	  break;
3387	}
3388      if (*args && !strcmp(args[0], "-l"))
3389	{
3390	  if (!args[1])
3391	    Msg(0, "encoding: -l: argument required");
3392	  else if (LoadFontTranslation(-1, args[1]))
3393	    Msg(0, "encoding: could not load utf8 encoding file");
3394	  else if (msgok)
3395	    Msg(0, "encoding: utf8 encoding file loaded");
3396	  break;
3397	}
3398#else
3399      if (*args && (!strcmp(args[0], "-l") || !strcmp(args[0], "-d")))
3400	{
3401	  if (msgok)
3402	    Msg(0, "encoding: screen is not compiled for UTF-8.");
3403	  break;
3404	}
3405#endif
3406      for (i = 0; i < 2; i++)
3407	{
3408	  if (args[i] == 0)
3409	    break;
3410	  if (!strcmp(args[i], "."))
3411	    continue;
3412	  n = FindEncoding(args[i]);
3413	  if (n == -1)
3414	    {
3415	      Msg(0, "encoding: unknown encoding '%s'", args[i]);
3416	      break;
3417	    }
3418	  if (i == 0 && fore)
3419	    {
3420	      WinSwitchEncoding(fore, n);
3421	      ResetCharsets(fore);
3422	    }
3423	  else if (i && display)
3424	    D_encoding  = n;
3425	}
3426      break;
3427    case RC_DEFKANJI:
3428    case RC_DEFENCODING:
3429      n = FindEncoding(*args);
3430      if (n == -1)
3431	{
3432	  Msg(0, "defencoding: unknown encoding '%s'", *args);
3433	  break;
3434	}
3435      nwin_default.encoding = n;
3436      break;
3437#endif
3438
3439#ifdef UTF8
3440    case RC_DEFUTF8:
3441      n = nwin_default.encoding == UTF8;
3442      if (ParseSwitch(act, &n) == 0)
3443	{
3444	  nwin_default.encoding = n ? UTF8 : 0;
3445	  if (msgok)
3446            Msg(0, "Will %suse UTF-8 encoding for new windows", n ? "" : "not ");
3447	}
3448      break;
3449    case RC_UTF8:
3450      for (i = 0; i < 2; i++)
3451	{
3452	  if (i && args[i] == 0)
3453	    break;
3454	  if (args[i] == 0)
3455	    n = fore->w_encoding != UTF8;
3456	  else if (strcmp(args[i], "off") == 0)
3457	    n = 0;
3458	  else if (strcmp(args[i], "on") == 0)
3459	    n = 1;
3460	  else
3461	    {
3462	      Msg(0, "utf8: illegal argument (%s)", args[i]);
3463	      break;
3464	    }
3465	  if (i == 0)
3466	    {
3467	      WinSwitchEncoding(fore, n ? UTF8 : 0);
3468	      if (msgok)
3469		Msg(0, "Will %suse UTF-8 encoding", n ? "" : "not ");
3470	    }
3471	  else if (display)
3472	    D_encoding = n ? UTF8 : 0;
3473	  if (args[i] == 0)
3474	    break;
3475	}
3476      break;
3477#endif
3478
3479    case RC_PRINTCMD:
3480      if (*args)
3481	{
3482	  if (printcmd)
3483	    free(printcmd);
3484	  printcmd = 0;
3485	  if (**args)
3486	    printcmd = SaveStr(*args);
3487	}
3488      if (*args == 0 || msgok)
3489	{
3490	  if (printcmd)
3491	    Msg(0, "using '%s' as print command", printcmd);
3492	  else
3493	    Msg(0, "using termcap entries for printing");
3494	    break;
3495	}
3496      break;
3497
3498    case RC_DIGRAPH:
3499      Input("Enter digraph: ", 10, INP_EVERY, digraph_fn, NULL);
3500      if (*args && **args)
3501	{
3502	  s = *args;
3503	  n = strlen(s);
3504	  LayProcess(&s, &n);
3505	}
3506      break;
3507
3508    case RC_DEFHSTATUS:
3509      if (*args == 0)
3510	{
3511	  char buf[256];
3512          *buf = 0;
3513	  if (nwin_default.hstatus)
3514            AddXChars(buf, sizeof(buf), nwin_default.hstatus);
3515	  Msg(0, "default hstatus is '%s'", buf);
3516	  break;
3517        }
3518      (void)ParseSaveStr(act, &nwin_default.hstatus);
3519      if (*nwin_default.hstatus == 0)
3520	{
3521	  free(nwin_default.hstatus);
3522	  nwin_default.hstatus = 0;
3523	}
3524      break;
3525    case RC_HSTATUS:
3526      (void)ParseSaveStr(act, &fore->w_hstatus);
3527      if (*fore->w_hstatus == 0)
3528	{
3529	  free(fore->w_hstatus);
3530	  fore->w_hstatus = 0;
3531	}
3532      WindowChanged(fore, 'h');
3533      break;
3534
3535#ifdef FONT
3536    case RC_DEFCHARSET:
3537    case RC_CHARSET:
3538      if (*args == 0)
3539        {
3540	  char buf[256];
3541          *buf = 0;
3542	  if (nwin_default.charset)
3543            AddXChars(buf, sizeof(buf), nwin_default.charset);
3544	  Msg(0, "default charset is '%s'", buf);
3545	  break;
3546        }
3547      n = strlen(*args);
3548      if (n == 0 || n > 6)
3549	{
3550	  Msg(0, "%s: %s: string has illegal size.", rc_name, comms[nr].name);
3551	  break;
3552	}
3553      if (n > 4 && (
3554        ((args[0][4] < '0' || args[0][4] > '3') && args[0][4] != '.') ||
3555        ((args[0][5] < '0' || args[0][5] > '3') && args[0][5] && args[0][5] != '.')))
3556	{
3557	  Msg(0, "%s: %s: illegal mapping number.", rc_name, comms[nr].name);
3558	  break;
3559	}
3560      if (nr == RC_CHARSET)
3561	{
3562	  SetCharsets(fore, *args);
3563	  break;
3564	}
3565      if (nwin_default.charset)
3566	free(nwin_default.charset);
3567      nwin_default.charset = SaveStr(*args);
3568      break;
3569#endif
3570#ifdef COLOR
3571    case RC_ATTRCOLOR:
3572      s = args[0];
3573      if (*s >= '0' && *s <= '9')
3574        i = *s - '0';
3575      else
3576	for (i = 0; i < 8; i++)
3577	  if (*s == "dubrsBiI"[i])
3578	    break;
3579      s++;
3580      nr = 0;
3581      if (*s && s[1] && !s[2])
3582	{
3583	  if (*s == 'd' && s[1] == 'd')
3584	    nr = 3;
3585	  else if (*s == '.' && s[1] == 'd')
3586	    nr = 2;
3587	  else if (*s == 'd' && s[1] == '.')
3588	    nr = 1;
3589	  else if (*s != '.' || s[1] != '.')
3590	    s--;
3591	  s += 2;
3592	}
3593      if (*s || i < 0 || i >= 8)
3594	{
3595	  Msg(0, "%s: attrcolor: unknown attribute '%s'.", rc_name, args[0]);
3596	  break;
3597	}
3598      n = 0;
3599      if (args[1])
3600        n = ParseAttrColor(args[1], args[2], 1);
3601      if (n == -1)
3602	break;
3603      attr2color[i][nr] = n;
3604      n = 0;
3605      for (i = 0; i < 8; i++)
3606	if (attr2color[i][0] || attr2color[i][1] || attr2color[i][2] || attr2color[i][3])
3607	  n |= 1 << i;
3608      nattr2color = n;
3609      break;
3610#endif
3611    case RC_SORENDITION:
3612      i = 0;
3613      if (*args)
3614	{
3615          i = ParseAttrColor(*args, args[1], 1);
3616	  if (i == -1)
3617	    break;
3618	  ApplyAttrColor(i, &mchar_so);
3619	  debug2("--> %x %x\n", mchar_so.attr, mchar_so.color);
3620	}
3621      if (msgok)
3622#ifdef COLOR
3623        Msg(0, "Standout attributes 0x%02x  color 0x%02x", (unsigned char)mchar_so.attr, 0x99 ^ (unsigned char)mchar_so.color);
3624#else
3625        Msg(0, "Standout attributes 0x%02x ", (unsigned char)mchar_so.attr);
3626#endif
3627      break;
3628
3629      case RC_SOURCE:
3630	do_source(*args);
3631	break;
3632
3633#ifdef MULTIUSER
3634    case RC_SU:
3635      s = NULL;
3636      if (!*args)
3637        {
3638	  Msg(0, "%s:%s screen login", HostName, SockPath);
3639          InputSu(D_fore, &D_user, NULL);
3640	}
3641      else if (!args[1])
3642        InputSu(D_fore, &D_user, args[0]);
3643      else if (!args[2])
3644        s = DoSu(&D_user, args[0], args[1], "\377");
3645      else
3646        s = DoSu(&D_user, args[0], args[1], args[2]);
3647      if (s)
3648        Msg(0, "%s", s);
3649      break;
3650#endif /* MULTIUSER */
3651    case RC_SPLIT:
3652      AddCanvas();
3653      Activate(-1);
3654      break;
3655    case RC_REMOVE:
3656      RemCanvas();
3657      Activate(-1);
3658      break;
3659    case RC_ONLY:
3660      OneCanvas();
3661      Activate(-1);
3662      break;
3663    case RC_FIT:
3664      D_forecv->c_xoff = D_forecv->c_xs;
3665      D_forecv->c_yoff = D_forecv->c_ys;
3666      RethinkViewportOffsets(D_forecv);
3667      ResizeLayer(D_forecv->c_layer, D_forecv->c_xe - D_forecv->c_xs + 1, D_forecv->c_ye - D_forecv->c_ys + 1, 0);
3668      flayer = D_forecv->c_layer;
3669      LaySetCursor();
3670      break;
3671    case RC_FOCUS:
3672      if (!*args || !strcmp(*args, "down"))
3673	D_forecv = D_forecv->c_next ? D_forecv->c_next : D_cvlist;
3674      else if (!strcmp(*args, "up"))
3675	{
3676	  struct canvas *cv;
3677	  for (cv = D_cvlist; cv->c_next && cv->c_next != D_forecv; cv = cv->c_next)
3678	    ;
3679	  D_forecv = cv;
3680	}
3681      else if (!strcmp(*args, "top"))
3682	D_forecv = D_cvlist;
3683      else if (!strcmp(*args, "bottom"))
3684	{
3685	  struct canvas *cv;
3686	  for (cv = D_cvlist; cv->c_next; cv = cv->c_next)
3687	    ;
3688	  D_forecv = cv;
3689	}
3690      else
3691	{
3692	  Msg(0, "%s: usage: focus [up|down|top|bottom]", rc_name);
3693	  break;
3694	}
3695      fore = D_fore = Layer2Window(D_forecv->c_layer);
3696      flayer = D_forecv->c_layer;
3697#ifdef RXVT_OSC
3698      if (D_xtermosc[2] || D_xtermosc[3])
3699	{
3700	  Activate(-1);
3701	  break;
3702	}
3703#endif
3704      RefreshHStatus();
3705#ifdef RXVT_OSC
3706      RefreshXtermOSC();
3707#endif
3708      flayer = D_forecv->c_layer;
3709      CV_CALL(D_forecv, LayRestore();LaySetCursor());
3710      WindowChanged(0, 'F');
3711      break;
3712    case RC_RESIZE:
3713      if (*args)
3714	ResizeRegions(*args);
3715      else
3716	Input("resize # lines: ", 20, INP_COOKED, ResizeFin, (char*)0);
3717      break;
3718    case RC_SETSID:
3719      (void)ParseSwitch(act, &separate_sids);
3720      break;
3721    case RC_EVAL:
3722      for (; *args; args++)
3723	{
3724	  char *ss = SaveStr(*args);
3725	  if (*ss)
3726	    Colonfin(ss, strlen(ss), (char *)0);
3727	  free(ss);
3728	}
3729      break;
3730    case RC_ALTSCREEN:
3731      (void)ParseSwitch(act, &use_altscreen);
3732      if (msgok)
3733        Msg(0, "Will %sdo alternate screen switching", use_altscreen ? "" : "not ");
3734      break;
3735    case RC_MAXWIN:
3736      if (ParseNum(act, &n))
3737	break;
3738      if (n < 1)
3739        Msg(0, "illegal maxwin number specified");
3740      else if (n > maxwin)
3741        Msg(0, "may only decrease maxwin number");
3742      else
3743        maxwin = n;
3744      break;
3745    case RC_BACKTICK:
3746      if (ParseBase(act, *args, &n, 10, "decimal"))
3747	break;
3748      if (!args[1])
3749        setbacktick(n, 0, 0, (char **)0);
3750      else
3751	{
3752	  int lifespan, tick;
3753	  if (argc < 4)
3754	    {
3755	      Msg(0, "%s: usage: backtick num [lifespan tick cmd args...]", rc_name);
3756	      break;
3757	    }
3758	  if (ParseBase(act, args[1], &lifespan, 10, "decimal"))
3759	    break;
3760	  if (ParseBase(act, args[2], &tick, 10, "decimal"))
3761	    break;
3762	  setbacktick(n, lifespan, tick, SaveArgs(args + 3));
3763	}
3764      WindowChanged(0, '`');
3765      break;
3766    case RC_BLANKER:
3767#ifdef BLANKER_PRG
3768      if (blankerprg)
3769	{
3770          RunBlanker(blankerprg);
3771	  break;
3772	}
3773#endif
3774      ClearAll();
3775      CursorVisibility(-1);
3776      D_blocked = 4;
3777      break;
3778#ifdef BLANKER_PRG
3779    case RC_BLANKERPRG:
3780      if (blankerprg)
3781	{
3782	  char **pp;
3783	  for (pp = blankerprg; *pp; pp++)
3784	    free(*pp);
3785	  free(blankerprg);
3786	  blankerprg = 0;
3787	}
3788      if (args[0][0])
3789	blankerprg = SaveArgs(args);
3790      break;
3791#endif
3792    case RC_IDLE:
3793      if (*args)
3794	{
3795	  struct display *olddisplay = display;
3796	  if (!strcmp(*args, "off"))
3797	    idletimo = 0;
3798	  else if (args[0][0])
3799	    idletimo = atoi(*args) * 1000;
3800	  if (argc > 1)
3801	    {
3802	      if ((i = FindCommnr(args[1])) == RC_ILLEGAL)
3803		{
3804		  Msg(0, "%s: idle: unknown command '%s'", rc_name, args[1]);
3805		  break;
3806		}
3807	      if (CheckArgNum(i, args + 2) < 0)
3808		break;
3809	      ClearAction(&idleaction);
3810	      SaveAction(&idleaction, i, args + 2, argl + 2);
3811	    }
3812	  for (display = displays; display; display = display->d_next)
3813	    ResetIdle();
3814	  display = olddisplay;
3815	}
3816      if (msgok)
3817	{
3818	  if (idletimo)
3819	    Msg(0, "idle timeout %ds, %s", idletimo / 1000, comms[idleaction.nr].name);
3820	  else
3821	    Msg(0, "idle off");
3822	}
3823      break;
3824    default:
3825#ifdef HAVE_BRAILLE
3826      /* key == -2: input from braille keybord, msgok always 0 */
3827      DoBrailleAction(act, key == -2 ? 0 : msgok);
3828#endif
3829      break;
3830    }
3831  if (display != odisplay)
3832    {
3833      for (display = displays; display; display = display->d_next)
3834        if (display == odisplay)
3835	  break;
3836    }
3837}
3838
3839void
3840DoCommand(argv, argl)
3841char **argv;
3842int *argl;
3843{
3844  struct action act;
3845
3846  if ((act.nr = FindCommnr(*argv)) == RC_ILLEGAL)
3847    {
3848      Msg(0, "%s: unknown command '%s'", rc_name, *argv);
3849      return;
3850    }
3851  act.args = argv + 1;
3852  act.argl = argl + 1;
3853  DoAction(&act, -1);
3854}
3855
3856static void
3857SaveAction(act, nr, args, argl)
3858struct action *act;
3859int nr;
3860char **args;
3861int *argl;
3862{
3863  register int argc = 0;
3864  char **pp;
3865  int *lp;
3866
3867  if (args)
3868    while (args[argc])
3869      argc++;
3870  if (argc == 0)
3871    {
3872      act->nr = nr;
3873      act->args = noargs;
3874      act->argl = 0;
3875      return;
3876    }
3877  if ((pp = (char **)malloc((unsigned)(argc + 1) * sizeof(char **))) == 0)
3878    Panic(0, strnomem);
3879  if ((lp = (int *)malloc((unsigned)(argc) * sizeof(int *))) == 0)
3880    Panic(0, strnomem);
3881  act->nr = nr;
3882  act->args = pp;
3883  act->argl = lp;
3884  while (argc--)
3885    {
3886      *lp = argl ? *argl++ : (int)strlen(*args);
3887      *pp++ = SaveStrn(*args++, *lp++);
3888    }
3889  *pp = 0;
3890}
3891
3892static char **
3893SaveArgs(args)
3894char **args;
3895{
3896  register char **ap, **pp;
3897  register int argc = 0;
3898
3899  while (args[argc])
3900    argc++;
3901  if ((pp = ap = (char **)malloc((unsigned)(argc + 1) * sizeof(char **))) == 0)
3902    Panic(0, strnomem);
3903  while (argc--)
3904    *pp++ = SaveStr(*args++);
3905  *pp = 0;
3906  return ap;
3907}
3908
3909
3910/*
3911 * buf is split into argument vector args.
3912 * leading whitespace is removed.
3913 * @!| abbreviations are expanded.
3914 * the end of buffer is recognized by '\0' or an un-escaped '#'.
3915 * " and ' are interpreted.
3916 *
3917 * argc is returned.
3918 */
3919int
3920Parse(buf, bufl, args, argl)
3921char *buf, **args;
3922int bufl, *argl;
3923{
3924  register char *p = buf, **ap = args, *pp;
3925  register int delim, argc;
3926  int *lp = argl;
3927
3928  debug2("Parse %d %s\n", bufl, buf);
3929  argc = 0;
3930  pp = buf;
3931  delim = 0;
3932  for (;;)
3933    {
3934      while (*p && (*p == ' ' || *p == '\t'))
3935	++p;
3936#ifdef PSEUDOS
3937      if (argc == 0 && *p == '!')
3938	{
3939	  *ap++ = "exec";
3940	  *lp++ = 4;
3941	  p++;
3942	  argc++;
3943	  continue;
3944        }
3945#endif
3946      if (*p == '\0' || *p == '#' || *p == '\n')
3947	{
3948	  *p = '\0';
3949	  for (delim = 0; delim < argc; delim++)
3950	    debug1("-- %s\n", args[delim]);
3951	  args[argc] = 0;
3952	  return argc;
3953	}
3954      if (++argc >= MAXARGS)
3955	{
3956	  Msg(0, "%s: too many tokens.", rc_name);
3957	  return 0;
3958	}
3959      *ap++ = pp;
3960
3961      debug1("- new arg %s\n", p);
3962      while (*p)
3963	{
3964	  if (*p == delim)
3965	    delim = 0;
3966	  else if (delim != '\'' && *p == '\\' && (p[1] == '\'' || p[1] == '"' || p[1] == '\\' || p[1] == '$' || p[1] == '#' || p[1] == '^' || (p[1] >= '0' && p[1] <= '7')))
3967	    {
3968	      p++;
3969	      if (*p >= '0' && *p <= '7')
3970		{
3971		  *pp = *p - '0';
3972		  if (p[1] >= '0' && p[1] <= '7')
3973		    {
3974		      p++;
3975		      *pp = (*pp << 3) | (*p - '0');
3976		      if (p[1] >= '0' && p[1] <= '7')
3977			{
3978			  p++;
3979			  *pp = (*pp << 3) | (*p - '0');
3980			}
3981		    }
3982		  pp++;
3983		}
3984	      else
3985		*pp++ = *p;
3986	    }
3987	  else if (delim != '\'' && *p == '$' && (p[1] == '{' || p[1] == ':' || (p[1] >= 'a' && p[1] <= 'z') || (p[1] >= 'A' && p[1] <= 'Z') || (p[1] >= '0' && p[1] <= '9') || p[1] == '_'))
3988
3989	    {
3990	      char *ps, *pe, op, *v, xbuf[11];
3991	      int vl;
3992
3993	      ps = ++p;
3994	      debug1("- var %s\n", ps);
3995	      p++;
3996	      while (*p)
3997		{
3998		  if (*ps == '{' && *p == '}')
3999		    break;
4000		  if (*ps == ':' && *p == ':')
4001		    break;
4002		  if (*ps != '{' && *ps != ':' && (*p < 'a' || *p > 'z') && (*p < 'A' || *p > 'Z') && (*p < '0' || *p > '9') && *p != '_')
4003		    break;
4004		  p++;
4005		}
4006	      pe = p;
4007	      if (*ps == '{' || *ps == ':')
4008		{
4009		  if (!*p)
4010		    {
4011		      Msg(0, "%s: bad variable name.", rc_name);
4012		      return 0;
4013		    }
4014		  p++;
4015		}
4016	      op = *pe;
4017	      *pe = 0;
4018	      debug1("- var is '%s'\n", ps);
4019	      if (*ps == ':')
4020		v = gettermcapstring(ps + 1);
4021	      else
4022		{
4023		  if (*ps == '{')
4024		    ps++;
4025		  v = xbuf;
4026		  if (!strcmp(ps, "TERM"))
4027		    v = display ? D_termname : "unknown";
4028		  else if (!strcmp(ps, "COLUMNS"))
4029		    sprintf(xbuf, "%d", display ? D_width : -1);
4030		  else if (!strcmp(ps, "LINES"))
4031		    sprintf(xbuf, "%d", display ? D_height : -1);
4032		  else
4033		    v = getenv(ps);
4034		}
4035	      *pe = op;
4036	      vl = v ? strlen(v) : 0;
4037	      if (vl)
4038		{
4039		  debug1("- sub is '%s'\n", v);
4040		  if (p - pp < vl)
4041		    {
4042		      int right = buf + bufl - (p + strlen(p) + 1);
4043		      if (right > 0)
4044			{
4045			  bcopy(p, p + right, strlen(p) + 1);
4046			  p += right;
4047			}
4048		    }
4049		  if (p - pp < vl)
4050		    {
4051		      Msg(0, "%s: no space left for variable expansion.", rc_name);
4052		      return 0;
4053		    }
4054		  bcopy(v, pp, vl);
4055		  pp += vl;
4056		}
4057	      continue;
4058	    }
4059	  else if (delim != '\'' && *p == '^' && p[1])
4060	    {
4061	      p++;
4062	      *pp++ = *p == '?' ? '\177' : *p & 0x1f;
4063	    }
4064	  else if (delim == 0 && (*p == '\'' || *p == '"'))
4065	    delim = *p;
4066	  else if (delim == 0 && (*p == ' ' || *p == '\t' || *p == '\n'))
4067	    break;
4068	  else
4069	    *pp++ = *p;
4070	  p++;
4071	}
4072      if (delim)
4073	{
4074	  Msg(0, "%s: Missing %c quote.", rc_name, delim);
4075	  return 0;
4076	}
4077      if (*p)
4078	p++;
4079      *pp = 0;
4080      debug2("- arg done, '%s' rest %s\n", ap[-1], p);
4081      *lp++ = pp - ap[-1];
4082      pp++;
4083    }
4084}
4085
4086void
4087SetEscape(u, e, me)
4088struct acluser *u;
4089int e, me;
4090{
4091  if (u)
4092    {
4093      u->u_Esc = e;
4094      u->u_MetaEsc = me;
4095    }
4096  else
4097    {
4098      if (users)
4099	{
4100	  if (DefaultEsc >= 0)
4101	    ClearAction(&ktab[DefaultEsc]);
4102	  if (DefaultMetaEsc >= 0)
4103	    ClearAction(&ktab[DefaultMetaEsc]);
4104	}
4105      DefaultEsc = e;
4106      DefaultMetaEsc = me;
4107      if (users)
4108	{
4109	  if (DefaultEsc >= 0)
4110	    {
4111	      ClearAction(&ktab[DefaultEsc]);
4112	      ktab[DefaultEsc].nr = RC_OTHER;
4113	    }
4114	  if (DefaultMetaEsc >= 0)
4115	    {
4116	      ClearAction(&ktab[DefaultMetaEsc]);
4117	      ktab[DefaultMetaEsc].nr = RC_META;
4118	    }
4119	}
4120    }
4121}
4122
4123int
4124ParseSwitch(act, var)
4125struct action *act;
4126int *var;
4127{
4128  if (*act->args == 0)
4129    {
4130      *var ^= 1;
4131      return 0;
4132    }
4133  return ParseOnOff(act, var);
4134}
4135
4136static int
4137ParseOnOff(act, var)
4138struct action *act;
4139int *var;
4140{
4141  register int num = -1;
4142  char **args = act->args;
4143
4144  if (args[1] == 0)
4145    {
4146      if (strcmp(args[0], "on") == 0)
4147	num = 1;
4148      else if (strcmp(args[0], "off") == 0)
4149	num = 0;
4150    }
4151  if (num < 0)
4152    {
4153      Msg(0, "%s: %s: invalid argument. Give 'on' or 'off'", rc_name, comms[act->nr].name);
4154      return -1;
4155    }
4156  *var = num;
4157  return 0;
4158}
4159
4160int
4161ParseSaveStr(act, var)
4162struct action *act;
4163char **var;
4164{
4165  char **args = act->args;
4166  if (*args == 0 || args[1])
4167    {
4168      Msg(0, "%s: %s: one argument required.", rc_name, comms[act->nr].name);
4169      return -1;
4170    }
4171  if (*var)
4172    free(*var);
4173  *var = SaveStr(*args);
4174  return 0;
4175}
4176
4177int
4178ParseNum(act, var)
4179struct action *act;
4180int *var;
4181{
4182  int i;
4183  char *p, **args = act->args;
4184
4185  p = *args;
4186  if (p == 0 || *p == 0 || args[1])
4187    {
4188      Msg(0, "%s: %s: invalid argument. Give one argument.",
4189          rc_name, comms[act->nr].name);
4190      return -1;
4191    }
4192  i = 0;
4193  while (*p)
4194    {
4195      if (*p >= '0' && *p <= '9')
4196	i = 10 * i + (*p - '0');
4197      else
4198	{
4199	  Msg(0, "%s: %s: invalid argument. Give numeric argument.",
4200	      rc_name, comms[act->nr].name);
4201	  return -1;
4202	}
4203      p++;
4204    }
4205  debug1("ParseNum got %d\n", i);
4206  *var = i;
4207  return 0;
4208}
4209
4210static int
4211ParseNum1000(act, var)
4212struct action *act;
4213int *var;
4214{
4215  int i;
4216  char *p, **args = act->args;
4217  int dig = 0;
4218
4219  p = *args;
4220  if (p == 0 || *p == 0 || args[1])
4221    {
4222      Msg(0, "%s: %s: invalid argument. Give one argument.",
4223          rc_name, comms[act->nr].name);
4224      return -1;
4225    }
4226  i = 0;
4227  while (*p)
4228    {
4229      if (*p >= '0' && *p <= '9')
4230	{
4231	  if (dig < 4)
4232	    i = 10 * i + (*p - '0');
4233          else if (dig == 4 && *p >= '5')
4234	    i++;
4235	  if (dig)
4236	    dig++;
4237	}
4238      else if (*p == '.' && !dig)
4239        dig++;
4240      else
4241	{
4242	  Msg(0, "%s: %s: invalid argument. Give floating point argument.",
4243	      rc_name, comms[act->nr].name);
4244	  return -1;
4245	}
4246      p++;
4247    }
4248  if (dig == 0)
4249    i *= 1000;
4250  else
4251    while (dig++ < 4)
4252      i *= 10;
4253  if (i < 0)
4254    i = (int)((unsigned int)~0 >> 1);
4255  debug1("ParseNum1000 got %d\n", i);
4256  *var = i;
4257  return 0;
4258}
4259
4260static struct win *
4261WindowByName(s)
4262char *s;
4263{
4264  struct win *p;
4265
4266  for (p = windows; p; p = p->w_next)
4267    if (!strcmp(p->w_title, s))
4268      return p;
4269  for (p = windows; p; p = p->w_next)
4270    if (!strncmp(p->w_title, s, strlen(s)))
4271      return p;
4272  return 0;
4273}
4274
4275static int
4276WindowByNumber(str)
4277char *str;
4278{
4279  int i;
4280  char *s;
4281
4282  for (i = 0, s = str; *s; s++)
4283    {
4284      if (*s < '0' || *s > '9')
4285        break;
4286      i = i * 10 + (*s - '0');
4287    }
4288  return *s ? -1 : i;
4289}
4290
4291/*
4292 * Get window number from Name or Number string.
4293 * Numbers are tried first, then names, a prefix match suffices.
4294 * Be careful when assigning numeric strings as WindowTitles.
4295 */
4296int
4297WindowByNoN(str)
4298char *str;
4299{
4300  int i;
4301  struct win *p;
4302
4303  if ((i = WindowByNumber(str)) < 0 || i >= MAXWIN)
4304    {
4305      if ((p = WindowByName(str)))
4306	return p->w_number;
4307      return -1;
4308    }
4309  return i;
4310}
4311
4312static int
4313ParseWinNum(act, var)
4314struct action *act;
4315int *var;
4316{
4317  char **args = act->args;
4318  int i = 0;
4319
4320  if (*args == 0 || args[1])
4321    {
4322      Msg(0, "%s: %s: one argument required.", rc_name, comms[act->nr].name);
4323      return -1;
4324    }
4325
4326  i = WindowByNoN(*args);
4327  if (i < 0)
4328    {
4329      Msg(0, "%s: %s: invalid argument. Give window number or name.",
4330          rc_name, comms[act->nr].name);
4331      return -1;
4332    }
4333  debug1("ParseWinNum got %d\n", i);
4334  *var = i;
4335  return 0;
4336}
4337
4338static int
4339ParseBase(act, p, var, base, bname)
4340struct action *act;
4341char *p;
4342int *var;
4343int base;
4344char *bname;
4345{
4346  int i = 0;
4347  int c;
4348
4349  if (*p == 0)
4350    {
4351      Msg(0, "%s: %s: empty argument.", rc_name, comms[act->nr].name);
4352      return -1;
4353    }
4354  while ((c = *p++))
4355    {
4356      if (c >= 'a' && c <= 'z')
4357	c -= 'a' - 'A';
4358      if (c >= 'A' && c <= 'Z')
4359	c -= 'A' - ('0' + 10);
4360      c -= '0';
4361      if (c < 0 || c >= base)
4362	{
4363	  Msg(0, "%s: %s: argument is not %s.", rc_name, comms[act->nr].name, bname);
4364	  return -1;
4365	}
4366      i = base * i + c;
4367    }
4368  debug1("ParseBase got %d\n", i);
4369  *var = i;
4370  return 0;
4371}
4372
4373static int
4374IsNum(s, base)
4375register char *s;
4376register int base;
4377{
4378  for (base += '0'; *s; ++s)
4379    if (*s < '0' || *s > base)
4380      return 0;
4381  return 1;
4382}
4383
4384int
4385IsNumColon(s, base, p, psize)
4386int base, psize;
4387char *s, *p;
4388{
4389  char *q;
4390  if ((q = rindex(s, ':')) != 0)
4391    {
4392      strncpy(p, q + 1, psize - 1);
4393      p[psize - 1] = '\0';
4394      *q = '\0';
4395    }
4396  else
4397    *p = '\0';
4398  return IsNum(s, base);
4399}
4400
4401void
4402SwitchWindow(n)
4403int n;
4404{
4405  struct win *p;
4406
4407  debug1("SwitchWindow %d\n", n);
4408  if (n < 0 || n >= MAXWIN)
4409    {
4410      ShowWindows(-1);
4411      return;
4412    }
4413  if ((p = wtab[n]) == 0)
4414    {
4415      ShowWindows(n);
4416      return;
4417    }
4418  if (display == 0)
4419    {
4420      fore = p;
4421      return;
4422    }
4423  if (p == D_fore)
4424    {
4425      Msg(0, "This IS window %d (%s).", n, p->w_title);
4426      return;
4427    }
4428#ifdef MULTIUSER
4429  if (AclCheckPermWin(D_user, ACL_READ, p))
4430    {
4431      Msg(0, "Access to window %d denied.", p->w_number);
4432      return;
4433    }
4434#endif
4435  SetForeWindow(p);
4436  Activate(fore->w_norefresh);
4437}
4438
4439
4440void
4441SetCanvasWindow(cv, wi)
4442struct canvas *cv;
4443struct win *wi;
4444{
4445  struct win *p = 0, **pp;
4446  struct layer *l;
4447  struct canvas *cvp, **cvpp;
4448
4449  l = cv->c_layer;
4450  display = cv->c_display;
4451
4452  if (l)
4453    {
4454      /* remove old layer */
4455      for (cvpp = &l->l_cvlist; (cvp = *cvpp); cvpp = &cvp->c_lnext)
4456	if (cvp == cv)
4457	  break;
4458      ASSERT(cvp);
4459      *cvpp = cvp->c_lnext;
4460
4461      p = Layer2Window(l);
4462      l = cv->c_layer;
4463      cv->c_layer = 0;
4464
4465      if (p && cv == D_forecv)
4466	{
4467#ifdef MULTIUSER
4468	  ReleaseAutoWritelock(display, p);
4469#endif
4470	  if (p->w_silence)
4471	    {
4472	      SetTimeout(&p->w_silenceev, p->w_silencewait * 1000);
4473	      evenq(&p->w_silenceev);
4474	    }
4475	  D_other = fore;
4476	  D_fore = 0;
4477	}
4478      if (l->l_cvlist == 0 && (p == 0 || l != p->w_savelayer))
4479	KillLayerChain(l);
4480    }
4481
4482  /* find right layer to display on canvas */
4483  if (wi)
4484    {
4485      l = &wi->w_layer;
4486      if (wi->w_savelayer && (wi->w_blocked || wi->w_savelayer->l_cvlist == 0))
4487	l = wi->w_savelayer;
4488    }
4489  else
4490    l = &cv->c_blank;
4491
4492  /* add our canvas to the layer's canvaslist */
4493  cv->c_lnext = l->l_cvlist;
4494  l->l_cvlist = cv;
4495  cv->c_layer = l;
4496  cv->c_xoff = cv->c_xs;
4497  cv->c_yoff = cv->c_ys;
4498  RethinkViewportOffsets(cv);
4499
4500  if (flayer == 0)
4501    flayer = l;
4502
4503  if (wi && D_other == wi)
4504    D_other = wi->w_next;	/* Might be 0, but that's OK. */
4505  if (cv == D_forecv)
4506    {
4507      D_fore = wi;
4508      fore = D_fore;	/* XXX ? */
4509      if (wi)
4510	{
4511#ifdef MULTIUSER
4512	  ObtainAutoWritelock(display, wi);
4513#endif
4514	  /*
4515	   * Place the window at the head of the most-recently-used list
4516	   */
4517	  if (windows != wi)
4518	    {
4519	      for (pp = &windows; (p = *pp); pp = &p->w_next)
4520		if (p == wi)
4521		  break;
4522	      ASSERT(p);
4523	      *pp = p->w_next;
4524	      p->w_next = windows;
4525	      windows = p;
4526	      WListLinkChanged();
4527	    }
4528	}
4529    }
4530}
4531
4532
4533/*
4534 * SetForeWindow changes the window in the input focus of the display.
4535 * Puts window wi in canvas display->d_forecv.
4536 */
4537void
4538SetForeWindow(wi)
4539struct win *wi;
4540{
4541  struct win *p;
4542  if (display == 0)
4543    {
4544      fore = wi;
4545      return;
4546    }
4547  p = Layer2Window(D_forecv->c_layer);
4548  SetCanvasWindow(D_forecv, wi);
4549  if (p)
4550    WindowChanged(p, 'u');
4551  if (wi)
4552    WindowChanged(wi, 'u');
4553  flayer = D_forecv->c_layer;
4554  /* Activate called afterwards, so no RefreshHStatus needed */
4555}
4556
4557
4558/*****************************************************************/
4559
4560/*
4561 *  Activate - make fore window active
4562 *  norefresh = -1 forces a refresh, disregard all_norefresh then.
4563 */
4564void
4565Activate(norefresh)
4566int norefresh;
4567{
4568  debug1("Activate(%d)\n", norefresh);
4569  if (display == 0)
4570    return;
4571  if (D_status)
4572    {
4573      Msg(0, "%s", "");	/* wait till mintime (keep gcc quiet) */
4574      RemoveStatus();
4575    }
4576
4577  if (MayResizeLayer(D_forecv->c_layer))
4578    ResizeLayer(D_forecv->c_layer, D_forecv->c_xe - D_forecv->c_xs + 1, D_forecv->c_ye - D_forecv->c_ys + 1, display);
4579
4580  fore = D_fore;
4581  if (fore)
4582    {
4583      /* XXX ? */
4584      if (fore->w_monitor != MON_OFF)
4585	fore->w_monitor = MON_ON;
4586      fore->w_bell = BELL_ON;
4587      WindowChanged(fore, 'f');
4588
4589#if 0
4590      if (ResizeDisplay(fore->w_width, fore->w_height))
4591	{
4592	  debug2("Cannot resize from (%d,%d)", D_width, D_height);
4593	  debug2(" to (%d,%d) -> resize window\n", fore->w_width, fore->w_height);
4594	  DoResize(D_width, D_height);
4595	}
4596#endif
4597    }
4598  Redisplay(norefresh + all_norefresh);
4599}
4600
4601
4602static int
4603NextWindow()
4604{
4605  register struct win **pp;
4606  int n = fore ? fore->w_number : -1;
4607
4608  for (pp = wtab + n + 1; pp != wtab + n; pp++)
4609    {
4610      if (pp == wtab + MAXWIN)
4611	pp = wtab;
4612      if (*pp)
4613	break;
4614    }
4615  return pp - wtab;
4616}
4617
4618static int
4619PreviousWindow()
4620{
4621  register struct win **pp;
4622  int n = fore ? fore->w_number : MAXWIN - 1;
4623
4624  for (pp = wtab + n - 1; pp != wtab + n; pp--)
4625    {
4626      if (pp < wtab)
4627	pp = wtab + MAXWIN - 1;
4628      if (*pp)
4629	break;
4630    }
4631  return pp - wtab;
4632}
4633
4634static int
4635MoreWindows()
4636{
4637  char *m = "No other window.";
4638  if (windows && (fore == 0 || windows->w_next))
4639    return 1;
4640  if (fore == 0)
4641    {
4642      Msg(0, "No window available");
4643      return 0;
4644    }
4645  Msg(0, m, fore->w_number);	/* other arg for nethack */
4646  return 0;
4647}
4648
4649void
4650KillWindow(wi)
4651struct win *wi;
4652{
4653  struct win **pp, *p;
4654  struct canvas *cv;
4655  int gotone;
4656
4657  /*
4658   * Remove window from linked list.
4659   */
4660  for (pp = &windows; (p = *pp); pp = &p->w_next)
4661    if (p == wi)
4662      break;
4663  ASSERT(p);
4664  *pp = p->w_next;
4665  wi->w_inlen = 0;
4666  wtab[wi->w_number] = 0;
4667
4668  if (windows == 0)
4669    {
4670      FreeWindow(wi);
4671      Finit(0);
4672    }
4673
4674  /*
4675   * switch to different window on all canvases
4676   */
4677  for (display = displays; display; display = display->d_next)
4678    {
4679      gotone = 0;
4680      for (cv = D_cvlist; cv; cv = cv->c_next)
4681	{
4682	  if (Layer2Window(cv->c_layer) != wi)
4683	    continue;
4684	  /* switch to other window */
4685	  SetCanvasWindow(cv, FindNiceWindow(D_other, 0));
4686	  gotone = 1;
4687	}
4688      if (gotone)
4689	{
4690#ifdef ZMODEM
4691	  if (wi->w_zdisplay == display)
4692	    {
4693	      D_blocked = 0;
4694	      D_readev.condpos = D_readev.condneg = 0;
4695	    }
4696#endif
4697	  Activate(-1);
4698	}
4699    }
4700  FreeWindow(wi);
4701  WindowChanged((struct win *)0, 'w');
4702  WindowChanged((struct win *)0, 'W');
4703  WindowChanged((struct win *)0, 0);
4704}
4705
4706static void
4707LogToggle(on)
4708int on;
4709{
4710  char buf[1024];
4711
4712  if ((fore->w_log != 0) == on)
4713    {
4714      if (display && !*rc_name)
4715	Msg(0, "You are %s logging.", on ? "already" : "not");
4716      return;
4717    }
4718  if (fore->w_log != 0)
4719    {
4720      Msg(0, "Logfile \"%s\" closed.", fore->w_log->name);
4721      logfclose(fore->w_log);
4722      fore->w_log = 0;
4723      WindowChanged(fore, 'f');
4724      return;
4725    }
4726  if (DoStartLog(fore, buf, sizeof(buf)))
4727    {
4728      Msg(errno, "Error opening logfile \"%s\"", buf);
4729      return;
4730    }
4731  if (ftell(fore->w_log->fp) == 0)
4732    Msg(0, "Creating logfile \"%s\".", fore->w_log->name);
4733  else
4734    Msg(0, "Appending to logfile \"%s\".", fore->w_log->name);
4735  WindowChanged(fore, 'f');
4736}
4737
4738char *
4739AddWindows(buf, len, flags, where)
4740char *buf;
4741int len;
4742int flags;
4743int where;
4744{
4745  register char *s, *ss;
4746  register struct win **pp, *p;
4747  register char *cmd;
4748  int l;
4749
4750  s = ss = buf;
4751  for (pp = ((flags & 4) && where >= 0) ? wtab + where + 1: wtab; pp < wtab + MAXWIN; pp++)
4752    {
4753      if (pp - wtab == where && ss == buf)
4754	ss = s;
4755      if ((p = *pp) == 0)
4756	continue;
4757      if ((flags & 1) && display && p == D_fore)
4758	continue;
4759
4760      cmd = p->w_title;
4761      l = strlen(cmd);
4762      if (l > 20)
4763        l = 20;
4764      if (s - buf + l > len - 24)
4765	break;
4766      if (s > buf || (flags & 4))
4767	{
4768	  *s++ = ' ';
4769	  *s++ = ' ';
4770	}
4771      sprintf(s, "%d", p->w_number);
4772      if (p->w_number == where)
4773        ss = s;
4774      s += strlen(s);
4775      if (display && p == D_fore)
4776	*s++ = '*';
4777      if (!(flags & 2))
4778	{
4779          if (display && p == D_other)
4780	    *s++ = '-';
4781          s = AddWindowFlags(s, len, p);
4782	}
4783      *s++ = ' ';
4784      strncpy(s, cmd, l);
4785      s += l;
4786    }
4787  *s = 0;
4788  return ss;
4789}
4790
4791char *
4792AddWindowFlags(buf, len, p)
4793char *buf;
4794int len;
4795struct win *p;
4796{
4797  char *s = buf;
4798  if (p == 0 || len < 12)
4799    {
4800      *s = 0;
4801      return s;
4802    }
4803#if 0
4804  if (display && p == D_fore)
4805    *s++ = '*';
4806  if (display && p == D_other)
4807    *s++ = '-';
4808#endif
4809  if (p->w_layer.l_cvlist && p->w_layer.l_cvlist->c_lnext)
4810    *s++ = '&';
4811  if (p->w_monitor == MON_DONE)
4812    *s++ = '@';
4813  if (p->w_bell == BELL_DONE)
4814    *s++ = '!';
4815#ifdef UTMPOK
4816  if (p->w_slot != (slot_t) 0 && p->w_slot != (slot_t) -1)
4817    *s++ = '$';
4818#endif
4819  if (p->w_log != 0)
4820    {
4821      strcpy(s, "(L)");
4822      s += 3;
4823    }
4824  if (p->w_ptyfd < 0)
4825    *s++ = 'Z';
4826  *s = 0;
4827  return s;
4828}
4829
4830char *
4831AddOtherUsers(buf, len, p)
4832char *buf;
4833int len;
4834struct win *p;
4835{
4836  struct display *d, *olddisplay = display;
4837  struct canvas *cv;
4838  char *s;
4839  int l;
4840
4841  s = buf;
4842  for (display = displays; display; display = display->d_next)
4843    {
4844      if (D_user == olddisplay->d_user)
4845	continue;
4846      for (cv = D_cvlist; cv; cv = cv->c_next)
4847	if (Layer2Window(cv->c_layer) == p)
4848	  break;
4849      if (!cv)
4850	continue;
4851      for (d = displays; d && d != display; d = d->d_next)
4852	if (D_user == d->d_user)
4853	  break;
4854      if (d && d != display)
4855	continue;
4856      if (len > 1 && s != buf)
4857	{
4858	  *s++ = ',';
4859	  len--;
4860	}
4861      l = strlen(D_user->u_name);
4862      if (l + 1 > len)
4863	break;
4864      strcpy(s, D_user->u_name);
4865      s += l;
4866      len -= l;
4867    }
4868  *s = 0;
4869  display = olddisplay;
4870  return s;
4871}
4872
4873void
4874ShowWindows(where)
4875int where;
4876{
4877  char buf[1024];
4878  char *s, *ss;
4879
4880  if (!display)
4881    return;
4882  if (where == -1 && D_fore)
4883    where = D_fore->w_number;
4884  ss = AddWindows(buf, sizeof(buf), 0, where);
4885  s = buf + strlen(buf);
4886  if (ss - buf > D_width / 2)
4887    {
4888      ss -= D_width / 2;
4889      if (s - ss < D_width)
4890	{
4891	  ss = s - D_width;
4892	  if (ss < buf)
4893	    ss = buf;
4894	}
4895    }
4896  else
4897    ss = buf;
4898  Msg(0, "%s", ss);
4899}
4900
4901static void
4902ShowInfo()
4903{
4904  char buf[512], *p;
4905  register struct win *wp = fore;
4906  register int i;
4907
4908  if (wp == 0)
4909    {
4910      Msg(0, "(%d,%d)/(%d,%d) no window", D_x + 1, D_y + 1, D_width, D_height);
4911      return;
4912    }
4913  p = buf;
4914  if (buf < (p += GetAnsiStatus(wp, p)))
4915    *p++ = ' ';
4916  sprintf(p, "(%d,%d)/(%d,%d)",
4917    wp->w_x + 1, wp->w_y + 1, wp->w_width, wp->w_height);
4918#ifdef COPY_PASTE
4919  sprintf(p += strlen(p), "+%d", wp->w_histheight);
4920#endif
4921  sprintf(p += strlen(p), " %c%sflow",
4922  	  (wp->w_flow & FLOW_NOW) ? '+' : '-',
4923	  (wp->w_flow & FLOW_AUTOFLAG) ? "" :
4924	   ((wp->w_flow & FLOW_AUTO) ? "(+)" : "(-)"));
4925  if (!wp->w_wrap) sprintf(p += strlen(p), " -wrap");
4926  if (wp->w_insert) sprintf(p += strlen(p), " ins");
4927  if (wp->w_origin) sprintf(p += strlen(p), " org");
4928  if (wp->w_keypad) sprintf(p += strlen(p), " app");
4929  if (wp->w_log)    sprintf(p += strlen(p), " log");
4930  if (wp->w_monitor != MON_OFF) sprintf(p += strlen(p), " mon");
4931  if (wp->w_mouse) sprintf(p += strlen(p), " mouse");
4932#ifdef COLOR
4933  if (wp->w_bce) sprintf(p += strlen(p), " bce");
4934#endif
4935  if (!wp->w_c1) sprintf(p += strlen(p), " -c1");
4936  if (wp->w_norefresh) sprintf(p += strlen(p), " nored");
4937
4938  p += strlen(p);
4939#ifdef FONT
4940# ifdef ENCODINGS
4941  if (wp->w_encoding && (display == 0 || D_encoding != wp->w_encoding || EncodingDefFont(wp->w_encoding) <= 0))
4942    {
4943      *p++ = ' ';
4944      strcpy(p, EncodingName(wp->w_encoding));
4945      p += strlen(p);
4946    }
4947#  ifdef UTF8
4948  if (wp->w_encoding != UTF8)
4949#  endif
4950# endif
4951    if (D_CC0 || (D_CS0 && *D_CS0))
4952      {
4953	if (wp->w_gr == 2)
4954	  {
4955	    sprintf(p, " G%c", wp->w_Charset + '0');
4956	    if (wp->w_FontE >= ' ')
4957	      p[3] = wp->w_FontE;
4958	    else
4959	      {
4960	        p[3] = '^';
4961	        p[4] = wp->w_FontE ^ 0x40;
4962		p++;
4963	      }
4964	    p[4] = '[';
4965	    p++;
4966	  }
4967	else if (wp->w_gr)
4968	  sprintf(p++, " G%c%c[", wp->w_Charset + '0', wp->w_CharsetR + '0');
4969	else
4970	  sprintf(p, " G%c[", wp->w_Charset + '0');
4971	p += 4;
4972	for (i = 0; i < 4; i++)
4973	  {
4974	    if (wp->w_charsets[i] == ASCII)
4975	      *p++ = 'B';
4976	    else if (wp->w_charsets[i] >= ' ')
4977	      *p++ = wp->w_charsets[i];
4978	    else
4979	      {
4980		*p++ = '^';
4981		*p++ = wp->w_charsets[i] ^ 0x40;
4982	      }
4983	  }
4984	*p++ = ']';
4985	*p = 0;
4986      }
4987#endif
4988
4989  if (wp->w_type == W_TYPE_PLAIN)
4990    {
4991      /* add info about modem control lines */
4992      *p++ = ' ';
4993      TtyGetModemStatus(wp->w_ptyfd, p);
4994    }
4995#ifdef BUILTIN_TELNET
4996  else if (wp->w_type == W_TYPE_TELNET)
4997    {
4998      *p++ = ' ';
4999      TelStatus(wp, p, sizeof(buf) - 1 - (p - buf));
5000    }
5001#endif
5002  Msg(0, "%s %d(%s)", buf, wp->w_number, wp->w_title);
5003}
5004
5005static void
5006ShowDInfo()
5007{
5008  char buf[512], *p;
5009  if (display == 0)
5010    return;
5011  p = buf;
5012  sprintf(p, "(%d,%d)", D_width, D_height),
5013  p += strlen(p);
5014#ifdef ENCODINGS
5015  if (D_encoding)
5016    {
5017      *p++ = ' ';
5018      strcpy(p, EncodingName(D_encoding));
5019      p += strlen(p);
5020    }
5021#endif
5022  if (D_CXT)
5023    {
5024      strcpy(p, " xterm");
5025      p += strlen(p);
5026    }
5027#ifdef COLOR
5028  if (D_hascolor)
5029    {
5030      strcpy(p, " color");
5031      p += strlen(p);
5032    }
5033#endif
5034#ifdef FONT
5035  if (D_CG0)
5036    {
5037      strcpy(p, " iso2022");
5038      p += strlen(p);
5039    }
5040  else if (D_CS0 && *D_CS0)
5041    {
5042      strcpy(p, " altchar");
5043      p += strlen(p);
5044    }
5045#endif
5046  Msg(0, "%s", buf);
5047}
5048
5049static void
5050AKAfin(buf, len, data)
5051char *buf;
5052int len;
5053char *data;	/* dummy */
5054{
5055  ASSERT(display);
5056  if (len && fore)
5057    ChangeAKA(fore, buf, strlen(buf));
5058}
5059
5060static void
5061InputAKA()
5062{
5063  char *s, *ss;
5064  int n;
5065  Input("Set window's title to: ", sizeof(fore->w_akabuf) - 1, INP_COOKED, AKAfin, NULL);
5066  s = fore->w_title;
5067  if (!s)
5068    return;
5069  for (; *s; s++)
5070    {
5071      if ((*(unsigned char *)s & 0x7f) < 0x20 || *s == 0x7f)
5072	continue;
5073      ss = s;
5074      n = 1;
5075      LayProcess(&ss, &n);
5076    }
5077}
5078
5079static void
5080Colonfin(buf, len, data)
5081char *buf;
5082int len;
5083char *data;	/* dummy */
5084{
5085  char mbuf[256];
5086  if (len)
5087    {
5088      len = strlen(buf) + 1;
5089      if (len > (int)sizeof(mbuf))
5090        RcLine(buf, len);
5091      else
5092	{
5093	  bcopy(buf, mbuf, len);
5094          RcLine(mbuf, sizeof mbuf);
5095	}
5096    }
5097}
5098
5099static void
5100SelectFin(buf, len, data)
5101char *buf;
5102int len;
5103char *data;	/* dummy */
5104{
5105  int n;
5106
5107  if (!len || !display)
5108    return;
5109  if (len == 1 && *buf == '-')
5110    {
5111      SetForeWindow((struct win *)0);
5112      Activate(0);
5113      return;
5114    }
5115  if ((n = WindowByNoN(buf)) < 0)
5116    return;
5117  SwitchWindow(n);
5118}
5119
5120static void
5121InputSelect()
5122{
5123  Input("Switch to window: ", 20, INP_COOKED, SelectFin, NULL);
5124}
5125
5126static char setenv_var[31];
5127
5128
5129static void
5130SetenvFin1(buf, len, data)
5131char *buf;
5132int len;
5133char *data;	/* dummy */
5134{
5135  if (!len || !display)
5136    return;
5137  InputSetenv(buf);
5138}
5139
5140static void
5141SetenvFin2(buf, len, data)
5142char *buf;
5143int len;
5144char *data;	/* dummy */
5145{
5146  if (!len || !display)
5147    return;
5148  debug2("SetenvFin2: setenv '%s' '%s'\n", setenv_var, buf);
5149  xsetenv(setenv_var, buf);
5150  MakeNewEnv();
5151}
5152
5153static void
5154InputSetenv(arg)
5155char *arg;
5156{
5157  static char setenv_buf[50 + sizeof(setenv_var)];	/* need to be static here, cannot be freed */
5158
5159  if (arg)
5160    {
5161      strncpy(setenv_var, arg, sizeof(setenv_var) - 1);
5162      sprintf(setenv_buf, "Enter value for %s: ", setenv_var);
5163      Input(setenv_buf, 30, INP_COOKED, SetenvFin2, NULL);
5164    }
5165  else
5166    Input("Setenv: Enter variable name: ", 30, INP_COOKED, SetenvFin1, NULL);
5167}
5168
5169/*
5170 * the following options are understood by this parser:
5171 * -f, -f0, -f1, -fy, -fa
5172 * -t title, -T terminal-type, -h height-of-scrollback,
5173 * -ln, -l0, -ly, -l1, -l
5174 * -a, -M, -L
5175 */
5176void
5177DoScreen(fn, av)
5178char *fn, **av;
5179{
5180  struct NewWindow nwin;
5181  register int num;
5182  char buf[20];
5183
5184  nwin = nwin_undef;
5185  while (av && *av && av[0][0] == '-')
5186    {
5187      if (av[0][1] == '-')
5188	{
5189	  av++;
5190	  break;
5191	}
5192      switch (av[0][1])
5193	{
5194	case 'f':
5195	  switch (av[0][2])
5196	    {
5197	    case 'n':
5198	    case '0':
5199	      nwin.flowflag = FLOW_NOW * 0;
5200	      break;
5201	    case 'y':
5202	    case '1':
5203	    case '\0':
5204	      nwin.flowflag = FLOW_NOW * 1;
5205	      break;
5206	    case 'a':
5207	      nwin.flowflag = FLOW_AUTOFLAG;
5208	      break;
5209	    default:
5210	      break;
5211	    }
5212	  break;
5213	case 't':	/* no more -k */
5214	  if (av[0][2])
5215	    nwin.aka = &av[0][2];
5216	  else if (*++av)
5217	    nwin.aka = *av;
5218	  else
5219	    --av;
5220	  break;
5221	case 'T':
5222	  if (av[0][2])
5223	    nwin.term = &av[0][2];
5224	  else if (*++av)
5225	    nwin.term = *av;
5226	  else
5227	    --av;
5228	  break;
5229	case 'h':
5230	  if (av[0][2])
5231	    nwin.histheight = atoi(av[0] + 2);
5232	  else if (*++av)
5233	    nwin.histheight = atoi(*av);
5234	  else
5235	    --av;
5236	  break;
5237#ifdef LOGOUTOK
5238	case 'l':
5239	  switch (av[0][2])
5240	    {
5241	    case 'n':
5242	    case '0':
5243	      nwin.lflag = 0;
5244	      break;
5245	    case 'y':
5246	    case '1':
5247	    case '\0':
5248	      nwin.lflag = 1;
5249	      break;
5250	    case 'a':
5251	      nwin.lflag = 3;
5252	      break;
5253	    default:
5254	      break;
5255	    }
5256	  break;
5257#endif
5258	case 'a':
5259	  nwin.aflag = 1;
5260	  break;
5261	case 'M':
5262	  nwin.monitor = MON_ON;
5263	  break;
5264	case 'L':
5265	  nwin.Lflag = 1;
5266	  break;
5267	default:
5268	  Msg(0, "%s: screen: invalid option -%c.", fn, av[0][1]);
5269	  break;
5270	}
5271      ++av;
5272    }
5273  num = 0;
5274  if (av && *av && IsNumColon(*av, 10, buf, sizeof(buf)))
5275    {
5276      if (*buf != '\0')
5277	nwin.aka = buf;
5278      num = atoi(*av);
5279      if (num < 0 || num > MAXWIN - 1)
5280	{
5281	  Msg(0, "%s: illegal screen number %d.", fn, num);
5282	  num = 0;
5283	}
5284      nwin.StartAt = num;
5285      ++av;
5286    }
5287  if (av && *av)
5288    {
5289      nwin.args = av;
5290      if (!nwin.aka)
5291        nwin.aka = Filename(*av);
5292    }
5293  MakeWindow(&nwin);
5294}
5295
5296#ifdef COPY_PASTE
5297/*
5298 * CompileKeys must be called before Markroutine is first used.
5299 * to initialise the keys with defaults, call CompileKeys(NULL, mark_key_tab);
5300 *
5301 * s is an ascii string in a termcap-like syntax. It looks like
5302 *   "j=u:k=d:l=r:h=l: =.:" and so on...
5303 * this example rebinds the cursormovement to the keys u (up), d (down),
5304 * l (left), r (right). placing a mark will now be done with ".".
5305 */
5306int
5307CompileKeys(s, sl, array)
5308char *s;
5309int sl;
5310unsigned char *array;
5311{
5312  int i;
5313  unsigned char key, value;
5314
5315  if (sl == 0)
5316    {
5317      for (i = 0; i < 256; i++)
5318        array[i] = i;
5319      return 0;
5320    }
5321  debug1("CompileKeys: '%s'\n", s);
5322  while (sl)
5323    {
5324      key = *(unsigned char *)s++;
5325      if (*s != '=' || sl < 3)
5326	return -1;
5327      sl--;
5328      do
5329	{
5330	  s++;
5331	  sl -= 2;
5332	  value = *(unsigned char *)s++;
5333	  array[value] = key;
5334	}
5335      while (*s == '=' && sl >= 2);
5336      if (sl == 0)
5337	break;
5338      if (*s++ != ':')
5339	return -1;
5340      sl--;
5341    }
5342  return 0;
5343}
5344#endif /* COPY_PASTE */
5345
5346/*
5347 *  Asynchronous input functions
5348 */
5349
5350#if defined(DETACH) && defined(POW_DETACH)
5351static void
5352pow_detach_fn(buf, len, data)
5353char *buf;
5354int len;
5355char *data;	/* dummy */
5356{
5357  debug("pow_detach_fn called\n");
5358  if (len)
5359    {
5360      *buf = 0;
5361      return;
5362    }
5363  if (ktab[(int)(unsigned char)*buf].nr != RC_POW_DETACH)
5364    {
5365      if (display)
5366        write(D_userfd, "\007", 1);
5367      Msg(0, "Detach aborted.");
5368    }
5369  else
5370    Detach(D_POWER);
5371}
5372#endif /* POW_DETACH */
5373
5374#ifdef COPY_PASTE
5375static void
5376copy_reg_fn(buf, len, data)
5377char *buf;
5378int len;
5379char *data;	/* dummy */
5380{
5381  struct plop *pp = plop_tab + (int)(unsigned char)*buf;
5382
5383  if (len)
5384    {
5385      *buf = 0;
5386      return;
5387    }
5388  if (pp->buf)
5389    free(pp->buf);
5390  pp->buf = 0;
5391  pp->len = 0;
5392  if (D_user->u_plop.len)
5393    {
5394      if ((pp->buf = (char *)malloc(D_user->u_plop.len)) == NULL)
5395	{
5396	  Msg(0, strnomem);
5397	  return;
5398	}
5399      bcopy(D_user->u_plop.buf, pp->buf, D_user->u_plop.len);
5400    }
5401  pp->len = D_user->u_plop.len;
5402#ifdef ENCODINGS
5403  pp->enc = D_user->u_plop.enc;
5404#endif
5405  Msg(0, "Copied %d characters into register %c", D_user->u_plop.len, *buf);
5406}
5407
5408static void
5409ins_reg_fn(buf, len, data)
5410char *buf;
5411int len;
5412char *data;	/* dummy */
5413{
5414  struct plop *pp = plop_tab + (int)(unsigned char)*buf;
5415
5416
5417  if (!fore)
5418    return;	/* Input() should not call us w/o fore, but you never know... */
5419  if (*buf == '.')
5420    Msg(0, "ins_reg_fn: Warning: pasting real register '.'!");
5421  if (len)
5422    {
5423      *buf = 0;
5424      return;
5425    }
5426  if (pp->buf)
5427    {
5428      MakePaster(&fore->w_paster, pp->buf, pp->len, 0);
5429      return;
5430    }
5431  Msg(0, "Empty register.");
5432}
5433#endif /* COPY_PASTE */
5434
5435static void
5436process_fn(buf, len, data)
5437char *buf;
5438int len;
5439char *data;	/* dummy */
5440{
5441  struct plop *pp = plop_tab + (int)(unsigned char)*buf;
5442
5443  if (len)
5444    {
5445      *buf = 0;
5446      return;
5447    }
5448  if (pp->buf)
5449    {
5450      ProcessInput(pp->buf, pp->len);
5451      return;
5452    }
5453  Msg(0, "Empty register.");
5454}
5455
5456static void
5457confirm_fn(buf, len, data)
5458char *buf;
5459int len;
5460char *data;	/* dummy */
5461{
5462  struct action act;
5463
5464  if (len || (*buf != 'y' && *buf != 'Y'))
5465    {
5466      *buf = 0;
5467      return;
5468    }
5469#ifdef __APPLE__
5470	act.nr = (int)(intptr_t)data;
5471#else
5472  act.nr = (int)data;
5473#endif
5474  act.args = noargs;
5475  act.argl = 0;
5476  DoAction(&act, -1);
5477}
5478
5479#ifdef MULTIUSER
5480struct inputsu
5481{
5482  struct acluser **up;
5483  char name[24];
5484  char pw1[130];	/* FreeBSD crypts to 128 bytes */
5485  char pw2[130];
5486};
5487
5488static void
5489su_fin(buf, len, data)
5490char *buf;
5491int len;
5492char *data;
5493{
5494  struct inputsu *i = (struct inputsu *)data;
5495  char *p;
5496  int l;
5497
5498  if (!*i->name)
5499    { p = i->name; l = sizeof(i->name) - 1; }
5500  else if (!*i->pw1)
5501    { strcpy(p = i->pw1, "\377"); l = sizeof(i->pw1) - 1; }
5502  else
5503    { strcpy(p = i->pw2, "\377"); l = sizeof(i->pw2) - 1; }
5504  if (buf && len)
5505    strncpy(p, buf, 1 + (l < len) ? l : len);
5506  if (!*i->name)
5507    Input("Screen User: ", sizeof(i->name) - 1, INP_COOKED, su_fin, (char *)i);
5508  else if (!*i->pw1)
5509    Input("User's UNIX Password: ", sizeof(i->pw1)-1, INP_COOKED|INP_NOECHO, su_fin, (char *)i);
5510  else if (!*i->pw2)
5511    Input("User's Screen Password: ", sizeof(i->pw2)-1, INP_COOKED|INP_NOECHO, su_fin, (char *)i);
5512  else
5513    {
5514      if ((p = DoSu(i->up, i->name, i->pw2, i->pw1)))
5515        Msg(0, "%s", p);
5516      free((char *)i);
5517    }
5518}
5519
5520static int
5521InputSu(w, up, name)
5522struct win *w;
5523struct acluser **up;
5524char *name;
5525{
5526  struct inputsu *i;
5527
5528  if (!(i = (struct inputsu *)calloc(1, sizeof(struct inputsu))))
5529    return -1;
5530
5531  i->up = up;
5532  if (name && *name)
5533    su_fin(name, (int)strlen(name), (char *)i); /* can also initialise stuff */
5534  else
5535    su_fin((char *)0, 0, (char *)i);
5536  return 0;
5537}
5538#endif	/* MULTIUSER */
5539
5540#ifdef PASSWORD
5541
5542static void
5543pass1(buf, len, data)
5544char *buf;
5545int len;
5546char *data;
5547{
5548  struct acluser *u = (struct acluser *)data;
5549
5550  if (!*buf)
5551    return;
5552  ASSERT(u);
5553  if (u->u_password != NullStr)
5554    free((char *)u->u_password);
5555  u->u_password = SaveStr(buf);
5556  bzero(buf, strlen(buf));
5557  Input("Retype new password:", 100, INP_NOECHO, pass2, data);
5558}
5559
5560static void
5561pass2(buf, len, data)
5562char *buf;
5563int len;
5564char *data;
5565{
5566  int st;
5567  char salt[3];
5568  struct acluser *u = (struct acluser *)data;
5569
5570  ASSERT(u);
5571  if (!buf || strcmp(u->u_password, buf))
5572    {
5573      Msg(0, "[ Passwords don't match - checking turned off ]");
5574      if (u->u_password != NullStr)
5575        {
5576          bzero(u->u_password, strlen(u->u_password));
5577          free((char *)u->u_password);
5578	}
5579      u->u_password = NullStr;
5580    }
5581  else if (u->u_password[0] == '\0')
5582    {
5583      Msg(0, "[ No password - no secure ]");
5584      if (buf)
5585        bzero(buf, strlen(buf));
5586    }
5587
5588  if (u->u_password != NullStr)
5589    {
5590      for (st = 0; st < 2; st++)
5591	salt[st] = 'A' + (int)((time(0) >> 6 * st) % 26);
5592      salt[2] = 0;
5593      buf = crypt(u->u_password, salt);
5594      bzero(u->u_password, strlen(u->u_password));
5595      free((char *)u->u_password);
5596      u->u_password = SaveStr(buf);
5597      bzero(buf, strlen(buf));
5598#ifdef COPY_PASTE
5599      if (u->u_plop.buf)
5600	UserFreeCopyBuffer(u);
5601      u->u_plop.len = strlen(u->u_password);
5602# ifdef ENCODINGS
5603      u->u_plop.enc = 0;
5604#endif
5605      if (!(u->u_plop.buf = SaveStr(u->u_password)))
5606	{
5607	  Msg(0, strnomem);
5608          D_user->u_plop.len = 0;
5609	}
5610      else
5611	Msg(0, "[ Password moved into copybuffer ]");
5612#else				/* COPY_PASTE */
5613      Msg(0, "[ Crypted password is \"%s\" ]", u->u_password);
5614#endif				/* COPY_PASTE */
5615    }
5616}
5617#endif /* PASSWORD */
5618
5619static void
5620digraph_fn(buf, len, data)
5621char *buf;
5622int len;
5623char *data;	/* dummy */
5624{
5625  int ch, i, x;
5626
5627  ch = buf[len];
5628  if (ch)
5629    {
5630      if (ch < ' ' || ch == '\177')
5631	return;
5632      if (len >= 1 && ((*buf == 'U' && buf[1] == '+') || (*buf == '0' && (buf[1] == 'x' || buf[1] == 'X'))))
5633	{
5634	  if (len == 1)
5635	    return;
5636	  if ((ch < '0' || ch > '9') && (ch < 'a' || ch > 'f') && (ch < 'A' || ch > 'F'))
5637	    {
5638	      buf[len] = '\034';	/* ^] is ignored by Input() */
5639	      return;
5640	    }
5641	  if (len == (*buf == 'U' ? 5 : 3))
5642	    buf[len] = '\n';
5643	  return;
5644	}
5645      if (len && *buf == '0')
5646	{
5647	  if (ch < '0' || ch > '7')
5648	    {
5649	      buf[len] = '\034';	/* ^] is ignored by Input() */
5650	      return;
5651	    }
5652	  if (len == 3)
5653	    buf[len] = '\n';
5654	  return;
5655	}
5656      if (len == 1)
5657        buf[len] = '\n';
5658      return;
5659    }
5660  buf[len] = buf[len + 1];	/* gross */
5661  len++;
5662  if (len < 2)
5663    return;
5664  if (len >= 1 && ((*buf == 'U' && buf[1] == '+') || (*buf == '0' && (buf[1] == 'x' || buf[1] == 'X'))))
5665    {
5666      x = 0;
5667      for (i = 2; i < len; i++)
5668	{
5669	  if (buf[i] >= '0' && buf[i] <= '9')
5670	    x = x * 16 | (buf[i] - '0');
5671	  else if (buf[i] >= 'a' && buf[i] <= 'f')
5672	    x = x * 16 | (buf[i] - ('a' - 10));
5673	  else if (buf[i] >= 'A' && buf[i] <= 'F')
5674	    x = x * 16 | (buf[i] - ('A' - 10));
5675	  else
5676	    break;
5677	}
5678    }
5679  else if (buf[0] == '0')
5680    {
5681      x = 0;
5682      for (i = 1; i < len; i++)
5683	{
5684	  if (buf[i] < '0' || buf[i] > '7')
5685	    break;
5686	  x = x * 8 | (buf[i] - '0');
5687	}
5688    }
5689  else
5690    {
5691      for (i = 0; i < (int)(sizeof(digraphs)/sizeof(*digraphs)); i++)
5692	if ((digraphs[i][0] == (unsigned char)buf[0] && digraphs[i][1] == (unsigned char)buf[1]) ||
5693	    (digraphs[i][0] == (unsigned char)buf[1] && digraphs[i][1] == (unsigned char)buf[0]))
5694	  break;
5695      if (i == (int)(sizeof(digraphs)/sizeof(*digraphs)))
5696	{
5697	  Msg(0, "Unknown digraph");
5698	  return;
5699	}
5700      x = digraphs[i][2];
5701    }
5702  i = 1;
5703  *buf = x;
5704#ifdef UTF8
5705  if (flayer->l_encoding == UTF8)
5706    i = ToUtf8(buf, x);	/* buf is big enough for all UTF-8 codes */
5707#endif
5708  while(i)
5709    LayProcess(&buf, &i);
5710}
5711
5712#ifdef MAPKEYS
5713int
5714StuffKey(i)
5715int i;
5716{
5717  struct action *act;
5718
5719  debug1("StuffKey #%d", i);
5720#ifdef DEBUG
5721  if (i < KMAP_KEYS)
5722    debug1(" - %s", term[i + T_CAPS].tcname);
5723#endif
5724  if (i >= T_CURSOR - T_CAPS && i < T_KEYPAD - T_CAPS && D_cursorkeys)
5725    i += T_OCAPS - T_CURSOR;
5726  else if (i >= T_KEYPAD - T_CAPS && i < T_OCAPS - T_CAPS && D_keypad)
5727    i += T_OCAPS - T_CURSOR;
5728  debug1(" - action %d\n", i);
5729  flayer = D_forecv->c_layer;
5730  fore = D_fore;
5731  act = 0;
5732#ifdef COPY_PASTE
5733  if (InMark() || InInput() || InWList())
5734    act = i < KMAP_KEYS+KMAP_AKEYS ? &mmtab[i] : &kmap_exts[i - (KMAP_KEYS+KMAP_AKEYS)].mm;
5735#endif
5736  if ((!act || act->nr == RC_ILLEGAL) && !D_mapdefault)
5737    act = i < KMAP_KEYS+KMAP_AKEYS ? &umtab[i] : &kmap_exts[i - (KMAP_KEYS+KMAP_AKEYS)].um;
5738  D_mapdefault = 0;
5739  if (!act || act->nr == RC_ILLEGAL)
5740    act = i < KMAP_KEYS+KMAP_AKEYS ? &dmtab[i] : &kmap_exts[i - (KMAP_KEYS+KMAP_AKEYS)].dm;
5741  if (act == 0 || act->nr == RC_ILLEGAL)
5742    return -1;
5743  DoAction(act, 0);
5744  return 0;
5745}
5746#endif
5747
5748
5749static int
5750IsOnDisplay(wi)
5751struct win *wi;
5752{
5753  struct canvas *cv;
5754  ASSERT(display);
5755  for (cv = D_cvlist; cv; cv = cv->c_next)
5756    if (Layer2Window(cv->c_layer) == wi)
5757      return 1;
5758  return 0;
5759}
5760
5761struct win *
5762FindNiceWindow(wi, presel)
5763struct win *wi;
5764char *presel;
5765{
5766  int i;
5767
5768  debug2("FindNiceWindow %d %s\n", wi ? wi->w_number : -1 , presel ? presel : "NULL");
5769  if (presel)
5770    {
5771      i = WindowByNoN(presel);
5772      if (i >= 0)
5773	wi = wtab[i];
5774    }
5775  if (!display)
5776    return wi;
5777#ifdef MULTIUSER
5778  if (wi && AclCheckPermWin(D_user, ACL_READ, wi))
5779    wi = 0;
5780#endif
5781  if (!wi || (IsOnDisplay(wi) && !presel))
5782    {
5783      /* try to get another window */
5784      wi = 0;
5785#ifdef MULTIUSER
5786      for (wi = windows; wi; wi = wi->w_next)
5787	if (!wi->w_layer.l_cvlist && !AclCheckPermWin(D_user, ACL_WRITE, wi))
5788	  break;
5789      if (!wi)
5790        for (wi = windows; wi; wi = wi->w_next)
5791	  if (wi->w_layer.l_cvlist && !IsOnDisplay(wi) && !AclCheckPermWin(D_user, ACL_WRITE, wi))
5792	    break;
5793      if (!wi)
5794	for (wi = windows; wi; wi = wi->w_next)
5795	  if (!wi->w_layer.l_cvlist && !AclCheckPermWin(D_user, ACL_READ, wi))
5796	    break;
5797      if (!wi)
5798	for (wi = windows; wi; wi = wi->w_next)
5799	  if (wi->w_layer.l_cvlist && !IsOnDisplay(wi) && !AclCheckPermWin(D_user, ACL_READ, wi))
5800	    break;
5801#endif
5802      if (!wi)
5803	for (wi = windows; wi; wi = wi->w_next)
5804	  if (!wi->w_layer.l_cvlist)
5805	    break;
5806      if (!wi)
5807	for (wi = windows; wi; wi = wi->w_next)
5808	  if (wi->w_layer.l_cvlist && !IsOnDisplay(wi))
5809	    break;
5810    }
5811#ifdef MULTIUSER
5812  if (wi && AclCheckPermWin(D_user, ACL_READ, wi))
5813    wi = 0;
5814#endif
5815  return wi;
5816}
5817
5818#if 0
5819
5820/* sorted list of all commands */
5821static struct comm **commtab;
5822static int ncommtab;
5823
5824void
5825AddComms(cos, hand)
5826struct comm *cos;
5827void (*hand) __P((struct comm *, char **, int));
5828{
5829  int n, i, j, r;
5830  for (n = 0; cos[n].name; n++)
5831    ;
5832  if (n == 0)
5833    return;
5834  if (commtab)
5835    commtab = (struct commt *)realloc(commtab, sizeof(*commtab) * (ncommtab + n));
5836  else
5837    commtab = (struct commt *)malloc(sizeof(*commtab) * (ncommtab + n));
5838  if (!commtab)
5839    Panic(0, strnomem);
5840  for (i = 0; i < n; i++)
5841    {
5842      for (j = 0; j < ncommtab; j++)
5843	{
5844	  r = strcmp(cos[i].name, commtab[j]->name);
5845	  if (r == 0)
5846	    Panic(0, "Duplicate command: %s\n", cos[i].name);
5847	  if (r < 0)
5848	    break;
5849	}
5850      for (r = ncommtab; r > j; r--)
5851	commtab[r] = commtab[r - 1];
5852      commtab[j] = cos + i;
5853      cos[i].handler = hand;
5854      bzero(cos[i].userbits, sizeof(cos[i].userbits));
5855      ncommtab++;
5856    }
5857}
5858
5859struct comm *
5860FindComm(str)
5861char *str;
5862{
5863  int x, m, l = 0, r = ncommtab - 1;
5864  while (l <= r)
5865    {
5866      m = (l + r) / 2;
5867      x = strcmp(str, commtab[m]->name);
5868      if (x > 0)
5869	l = m + 1;
5870      else if (x < 0)
5871	r = m - 1;
5872      else
5873	return commtab[m];
5874    }
5875  return 0;
5876}
5877
5878#endif
5879
5880static void
5881ResizeRegions(arg)
5882char *arg;
5883{
5884  struct canvas *cv;
5885  int nreg, dsize, diff, siz;
5886
5887  ASSERT(display);
5888  for (nreg = 0, cv = D_cvlist; cv; cv = cv->c_next)
5889    nreg++;
5890  if (nreg < 2)
5891    {
5892      Msg(0, "resize: need more than one region");
5893      return;
5894    }
5895  dsize = D_height - (D_has_hstatus == HSTATUS_LASTLINE);
5896  if (*arg == '=')
5897    {
5898      /* make all regions the same height */
5899      int h = dsize;
5900      int hh, i = 0;
5901      for (cv = D_cvlist; cv; cv = cv->c_next)
5902	{
5903	  hh = h / nreg-- - 1;
5904	  cv->c_ys = i;
5905	  cv->c_ye = i + hh - 1;
5906	  cv->c_yoff = i;
5907	  i += hh + 1;
5908	  h -= hh + 1;
5909        }
5910      RethinkDisplayViewports();
5911      ResizeLayersToCanvases();
5912      return;
5913    }
5914  siz = D_forecv->c_ye - D_forecv->c_ys + 1;
5915  if (*arg == '+')
5916    diff = atoi(arg + 1);
5917  else if (*arg == '-')
5918    diff = -atoi(arg + 1);
5919  else if (!strcmp(arg, "min"))
5920    diff = 1 - siz;
5921  else if (!strcmp(arg, "max"))
5922    diff = dsize - (nreg - 1) * 2 - 1 - siz;
5923  else
5924    diff = atoi(arg) - siz;
5925  if (diff == 0)
5926    return;
5927  if (siz + diff < 1)
5928    diff = 1 - siz;
5929  if (siz + diff > dsize - (nreg - 1) * 2 - 1)
5930    diff = dsize - (nreg - 1) * 2 - 1 - siz;
5931  if (diff == 0 || siz + diff < 1)
5932    return;
5933
5934  if (diff < 0)
5935    {
5936      if (D_forecv->c_next)
5937	{
5938	  D_forecv->c_ye += diff;
5939	  D_forecv->c_next->c_ys += diff;
5940	  D_forecv->c_next->c_yoff += diff;
5941	}
5942      else
5943	{
5944	  for (cv = D_cvlist; cv; cv = cv->c_next)
5945	    if (cv->c_next == D_forecv)
5946	      break;
5947	  ASSERT(cv);
5948	  cv->c_ye -= diff;
5949	  D_forecv->c_ys -= diff;
5950	  D_forecv->c_yoff -= diff;
5951	}
5952    }
5953  else
5954    {
5955      int s, i = 0, found = 0, di = diff, d2;
5956      s = dsize - (nreg - 1) * 2 - 1 - siz;
5957      for (cv = D_cvlist; cv; i = cv->c_ye + 2, cv = cv->c_next)
5958	{
5959	  if (cv == D_forecv)
5960	    {
5961	      cv->c_ye = i + (cv->c_ye - cv->c_ys) + diff;
5962	      cv->c_yoff -= cv->c_ys - i;
5963	      cv->c_ys = i;
5964	      found = 1;
5965	      continue;
5966	    }
5967	  s -= cv->c_ye - cv->c_ys;
5968	  if (!found)
5969	    {
5970	      if (s >= di)
5971		continue;
5972	      d2 = di - s;
5973	    }
5974	  else
5975	    d2 = di > cv->c_ye - cv->c_ys ? cv->c_ye - cv->c_ys : di;
5976	  di -= d2;
5977	  cv->c_ye = i + (cv->c_ye - cv->c_ys) - d2;
5978	  cv->c_yoff -= cv->c_ys - i;
5979	  cv->c_ys = i;
5980        }
5981    }
5982  RethinkDisplayViewports();
5983  ResizeLayersToCanvases();
5984}
5985
5986static void
5987ResizeFin(buf, len, data)
5988char *buf;
5989int len;
5990char *data;
5991{
5992  ResizeRegions(buf);
5993}
5994
5995#ifdef RXVT_OSC
5996void
5997RefreshXtermOSC()
5998{
5999  int i;
6000  struct win *p;
6001
6002  p = Layer2Window(D_forecv->c_layer);
6003  for (i = 3; i >=0; i--)
6004    SetXtermOSC(i, p ? p->w_xtermosc[i] : 0);
6005}
6006#endif
6007
6008int
6009ParseAttrColor(s1, s2, msgok)
6010char *s1, *s2;
6011int msgok;
6012{
6013  int i, n;
6014  char *s, *ss;
6015  int r = 0;
6016
6017  s = s1;
6018  while (*s == ' ')
6019    s++;
6020  ss = s;
6021  while (*ss && *ss != ' ')
6022    ss++;
6023  while (*ss == ' ')
6024    ss++;
6025  if (*s && (s2 || *ss || !((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || *s == '.')))
6026    {
6027      int mode = 0, n = 0;
6028      if (*s == '+')
6029	{
6030	  mode = 1;
6031	  s++;
6032	}
6033      else if (*s == '-')
6034	{
6035	  mode = -1;
6036	  s++;
6037	}
6038      else if (*s == '!')
6039	{
6040	  mode = 2;
6041	  s++;
6042	}
6043      else if (*s == '=')
6044	s++;
6045      if (*s >= '0' && *s <= '9')
6046	{
6047	  n = *s++ - '0';
6048	  if (*s >= '0' && *s <= '9')
6049	    n = n * 16 + (*s++ - '0');
6050	  else if (*s >= 'a' && *s <= 'f')
6051	    n = n * 16 + (*s++ - ('a' - 10));
6052	  else if (*s >= 'A' && *s <= 'F')
6053	    n = n * 16 + (*s++ - ('A' - 10));
6054	  else if (*s && *s != ' ')
6055	    {
6056	      if (msgok)
6057		Msg(0, "Illegal attribute hexchar '%c'", *s);
6058	      return -1;
6059	    }
6060	}
6061      else
6062	{
6063	  while (*s && *s != ' ')
6064	    {
6065	      if (*s == 'd')
6066		n |= A_DI;
6067	      else if (*s == 'u')
6068		n |= A_US;
6069	      else if (*s == 'b')
6070		n |= A_BD;
6071	      else if (*s == 'r')
6072		n |= A_RV;
6073	      else if (*s == 's')
6074		n |= A_SO;
6075	      else if (*s == 'B')
6076		n |= A_BL;
6077	      else
6078		{
6079		  if (msgok)
6080		    Msg(0, "Illegal attribute specifier '%c'", *s);
6081		  return -1;
6082		}
6083	      s++;
6084	    }
6085	}
6086      if (*s && *s != ' ')
6087	{
6088	  if (msgok)
6089	    Msg(0, "junk after attribute description: '%c'", *s);
6090	  return -1;
6091	}
6092      if (mode == -1)
6093	r = n << 8 | n;
6094      else if (mode == 1)
6095	r = n << 8;
6096      else if (mode == 2)
6097	r = n;
6098      else if (mode == 0)
6099	r = 0xffff ^ n;
6100    }
6101  while (*s && *s == ' ')
6102    s++;
6103
6104  if (s2)
6105    {
6106      if (*s)
6107	{
6108	  if (msgok)
6109	    Msg(0, "junk after description: '%c'", *s);
6110	  return -1;
6111	}
6112      s = s2;
6113      while (*s && *s == ' ')
6114	s++;
6115    }
6116
6117#ifdef COLOR
6118  if (*s)
6119    {
6120      static char costr[] = "krgybmcw d    i.01234567 9     f               FKRGYBMCW      I ";
6121      int numco = 0, j;
6122
6123      n = 0;
6124      if (*s == '.')
6125	{
6126	  numco++;
6127	  n = 0x0f;
6128	  s++;
6129	}
6130      for (j = 0; j < 2 && *s && *s != ' '; j++)
6131	{
6132	  for (i = 0; costr[i]; i++)
6133	    if (*s == costr[i])
6134	      break;
6135	  if (!costr[i])
6136	    {
6137	      if (msgok)
6138		Msg(0, "illegal color descriptor: '%c'", *s);
6139	      return -1;
6140	    }
6141	  numco++;
6142	  n = n << 4 | (i & 15);
6143#ifdef COLORS16
6144	  if (i >= 48)
6145	    n = (n & 0x20ff) | 0x200;
6146#endif
6147	  s++;
6148	}
6149      if ((n & 0xf00) == 0xf00)
6150        n ^= 0xf00;	/* clear superflous bits */
6151#ifdef COLORS16
6152      if (n & 0x2000)
6153	n ^= 0x2400;	/* shift bit into right position */
6154#endif
6155      if (numco == 1)
6156	n |= 0xf0;	/* don't change bg color */
6157      if (numco != 2 && n != 0xff)
6158	n |= 0x100;	/* special invert mode */
6159      if (*s && *s != ' ')
6160	{
6161	  if (msgok)
6162	    Msg(0, "junk after color description: '%c'", *s);
6163	  return -1;
6164	}
6165      n ^= 0xff;
6166      r |= n << 16;
6167    }
6168#endif
6169
6170  while (*s && *s == ' ')
6171    s++;
6172  if (*s)
6173    {
6174      if (msgok)
6175	Msg(0, "junk after description: '%c'", *s);
6176      return -1;
6177    }
6178  debug1("ParseAttrColor %06x\n", r);
6179  return r;
6180}
6181
6182/*
6183 *  Color coding:
6184 *    0-7 normal colors
6185 *    9   default color
6186 *    e   just set intensity
6187 *    f   don't change anything
6188 *  Intensity is encoded into bits 17(fg) and 18(bg).
6189 */
6190void
6191ApplyAttrColor(i, mc)
6192int i;
6193struct mchar *mc;
6194{
6195  debug1("ApplyAttrColor %06x\n", i);
6196  mc->attr |= i >> 8 & 255;
6197  mc->attr ^= i & 255;
6198#ifdef COLOR
6199  i = (i >> 16) ^ 0xff;
6200  if ((i & 0x100) != 0)
6201    {
6202      i &= 0xeff;
6203      if (mc->attr & (A_SO|A_RV))
6204# ifdef COLORS16
6205        i = ((i & 0x0f) << 4) | ((i & 0xf0) >> 4) | ((i & 0x200) << 1) | ((i & 0x400) >> 1);
6206# else
6207        i = ((i & 0x0f) << 4) | ((i & 0xf0) >> 4);
6208# endif
6209    }
6210# ifdef COLORS16
6211  if ((i & 0x0f) != 0x0f)
6212    mc->attr = (mc->attr & 0xbf) | ((i >> 3) & 0x40);
6213  if ((i & 0xf0) != 0xf0)
6214    mc->attr = (mc->attr & 0x7f) | ((i >> 3) & 0x80);
6215# endif
6216  mc->color = 0x99 ^ mc->color;
6217  if ((i & 0x0e) == 0x0e)
6218    i = (i & 0xf0) | (mc->color & 0x0f);
6219  if ((i & 0xe0) == 0xe0)
6220    i = (i & 0x0f) | (mc->color & 0xf0);
6221  mc->color = 0x99 ^ i;
6222  debug2("ApplyAttrColor - %02x %02x\n", mc->attr, i);
6223#endif
6224}
6225