Fix some more windowlist glitches
[screen-lua.git] / src / screen.c
blob110b5cc38867a1b6d099c36d194e1f38d3a72071
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 #ifdef HAVE_BRAILLE
6 * Modified by:
7 * Authors: Hadi Bargi Rangin bargi@dots.physics.orst.edu
8 * Bill Barry barryb@dots.physics.orst.edu
9 * Randy Lundquist randyl@dots.physics.orst.edu
11 * Modifications Copyright (c) 1995 by
12 * Science Access Project, Oregon State University.
13 #endif
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2, or (at your option)
18 * any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program (see the file COPYING); if not, write to the
27 * Free Software Foundation, Inc.,
28 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
30 ****************************************************************
33 #include <sys/types.h>
34 #include <ctype.h>
36 #include <fcntl.h>
38 #ifdef sgi
39 # include <sys/sysmacros.h>
40 #endif
42 #include <sys/stat.h>
43 #ifndef sun
44 # include <sys/ioctl.h>
45 #endif
47 #ifndef SIGINT
48 # include <signal.h>
49 #endif
51 #include "config.h"
53 #ifdef HAVE_STROPTS_H
54 # include <sys/stropts.h>
55 #endif
57 #if defined(SYSV) && !defined(ISC)
58 # include <sys/utsname.h>
59 #endif
61 #if defined(sequent) || defined(SVR4)
62 # include <sys/resource.h>
63 #endif /* sequent || SVR4 */
65 #ifdef ISC
66 # include <sys/tty.h>
67 # include <sys/sioctl.h>
68 # include <sys/pty.h>
69 #endif /* ISC */
71 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
72 # include <compat.h>
73 #endif
74 #if defined(USE_LOCALE) || defined(ENCODINGS)
75 # include <locale.h>
76 #endif
77 #if defined(HAVE_NL_LANGINFO) && defined(ENCODINGS)
78 # include <langinfo.h>
79 #endif
81 #include "screen.h"
82 #ifdef HAVE_BRAILLE
83 # include "braille.h"
84 #endif
86 #include "patchlevel.h"
89 * At the moment we only need the real password if the
90 * builtin lock is used. Therefore disable SHADOWPW if
91 * we do not really need it (kind of security thing).
93 #ifndef LOCK
94 # undef SHADOWPW
95 #endif
97 #include <pwd.h>
98 #ifdef SHADOWPW
99 # include <shadow.h>
100 #endif /* SHADOWPW */
102 #include "logfile.h" /* islogfile, logfflush */
104 #ifdef DEBUG
105 FILE *dfp;
106 #endif
109 extern char Term[], screenterm[], **environ, Termcap[];
110 int force_vt = 1;
111 int VBellWait, MsgWait, MsgMinWait, SilenceWait;
113 extern struct acluser *users;
114 extern struct display *displays, *display;
117 extern int visual_bell;
118 #ifdef COPY_PASTE
119 extern unsigned char mark_key_tab[];
120 #endif
121 extern char version[];
122 extern char DefaultShell[];
123 #ifdef ZMODEM
124 extern char *zmodem_sendcmd;
125 extern char *zmodem_recvcmd;
126 #endif
127 extern struct layout *layout_last;
130 char *ShellProg;
131 char *ShellArgs[2];
133 extern struct NewWindow nwin_undef, nwin_default, nwin_options;
134 struct backtick;
136 static struct passwd *getpwbyname __P((char *, struct passwd *));
137 static void SigChldHandler __P((void));
138 static sigret_t SigChld __P(SIGPROTOARG);
139 static sigret_t SigInt __P(SIGPROTOARG);
140 static sigret_t CoreDump __P(SIGPROTOARG);
141 static sigret_t FinitHandler __P(SIGPROTOARG);
142 static void DoWait __P((void));
143 static void serv_read_fn __P((struct event *, char *));
144 static void serv_select_fn __P((struct event *, char *));
145 static void logflush_fn __P((struct event *, char *));
146 static void backtick_filter __P((struct backtick *));
147 static void backtick_fn __P((struct event *, char *));
148 static char *runbacktick __P((struct backtick *, int *, time_t));
149 static int IsSymbol __P((char *, char *));
150 static char *ParseChar __P((char *, char *));
151 static int ParseEscape __P((char *));
152 static char *pad_expand __P((char *, char *, int, int));
153 #ifdef DEBUG
154 static void fds __P((void));
155 #endif
157 int nversion; /* numerical version, used for secondary DA */
159 /* the attacher */
160 struct passwd *ppp;
161 char *attach_tty;
162 int attach_fd = -1;
163 char *attach_term;
164 char *LoginName;
165 struct mode attach_Mode;
167 char SockPath[MAXPATHLEN + 2 * MAXSTR];
168 char *SockName; /* SockName is pointer in SockPath */
169 char *SockMatch = NULL; /* session id command line argument */
170 int ServerSocket = -1;
171 struct event serv_read;
172 struct event serv_select;
173 struct event logflushev;
175 char **NewEnv = NULL;
177 char *RcFileName = NULL;
178 char *home;
180 char *screenlogfile; /* filename layout */
181 int log_flush = 10; /* flush interval in seconds */
182 int logtstamp_on = 0; /* tstamp disabled */
183 char *logtstamp_string; /* stamp layout */
184 int logtstamp_after = 120; /* first tstamp after 120s */
185 char *hardcopydir = NULL;
186 char *BellString;
187 char *VisualBellString;
188 char *ActivityString;
189 #ifdef COPY_PASTE
190 char *BufferFile;
191 #endif
192 #ifdef POW_DETACH
193 char *PowDetachString;
194 #endif
195 char *hstatusstring;
196 char *captionstring;
197 char *timestring;
198 char *wliststr;
199 char *wlisttit;
200 int auto_detach = 1;
201 int iflag, rflag, dflag, lsflag, quietflag, wipeflag, xflag;
202 int cmdflag;
203 int adaptflag;
205 #ifdef MULTIUSER
206 char *multi;
207 char *multi_home;
208 int multi_uid;
209 int own_uid;
210 int multiattach;
211 int tty_mode;
212 int tty_oldmode = -1;
213 #endif
215 char HostName[MAXSTR];
216 int MasterPid, PanicPid;
217 int real_uid, real_gid, eff_uid, eff_gid;
218 int default_startup;
219 int ZombieKey_destroy, ZombieKey_resurrect, ZombieKey_onerror;
220 char *preselect = NULL; /* only used in Attach() */
222 #ifdef UTF8
223 char *screenencodings;
224 #endif
226 #ifdef DW_CHARS
227 int cjkwidth;
228 #endif
230 #ifdef NETHACK
231 int nethackflag = 0;
232 #endif
233 int maxwin = MAXWIN;
236 struct layer *flayer;
237 struct win *fore;
238 struct win *windows;
239 struct win *console_window;
244 * Do this last
246 #include "extern.h"
248 char strnomem[] = "Out of memory.";
251 static int InterruptPlease;
252 static int GotSigChld;
254 static int
255 lf_secreopen(name, wantfd, l)
256 char *name;
257 int wantfd;
258 struct logfile *l;
260 int got_fd;
262 close(wantfd);
263 if (((got_fd = secopen(name, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) ||
264 lf_move_fd(got_fd, wantfd) < 0)
266 logfclose(l);
267 debug1("lf_secreopen: failed for %s\n", name);
268 return -1;
270 l->st->st_ino = l->st->st_dev = 0;
271 debug2("lf_secreopen: %d = %s\n", wantfd, name);
272 return 0;
275 /********************************************************************/
276 /********************************************************************/
277 /********************************************************************/
280 static struct passwd *
281 getpwbyname(name, ppp)
282 char *name;
283 struct passwd *ppp;
285 int n;
286 #ifdef SHADOWPW
287 struct spwd *sss = NULL;
288 static char *spw = NULL;
289 #endif
291 if (!ppp && !(ppp = getpwnam(name)))
292 return NULL;
294 /* Do password sanity check..., allow ##user for SUN_C2 security */
295 #ifdef SHADOWPW
296 pw_try_again:
297 #endif
298 n = 0;
299 if (ppp->pw_passwd[0] == '#' && ppp->pw_passwd[1] == '#' &&
300 strcmp(ppp->pw_passwd + 2, ppp->pw_name) == 0)
301 n = 13;
302 for (; n < 13; n++)
304 char c = ppp->pw_passwd[n];
305 if (!(c == '.' || c == '/' || c == '$' ||
306 (c >= '0' && c <= '9') ||
307 (c >= 'a' && c <= 'z') ||
308 (c >= 'A' && c <= 'Z')))
309 break;
312 #ifdef SHADOWPW
313 /* try to determine real password */
314 if (n < 13 && sss == 0)
316 sss = getspnam(ppp->pw_name);
317 if (sss)
319 if (spw)
320 free(spw);
321 ppp->pw_passwd = spw = SaveStr(sss->sp_pwdp);
322 endspent(); /* this should delete all buffers ... */
323 goto pw_try_again;
325 endspent(); /* this should delete all buffers ... */
327 #endif
328 if (n < 13)
329 ppp->pw_passwd = 0;
330 #ifdef linux
331 if (ppp->pw_passwd && strlen(ppp->pw_passwd) == 13 + 11)
332 ppp->pw_passwd[13] = 0; /* beware of linux's long passwords */
333 #endif
335 return ppp;
340 main(ac, av)
341 int ac;
342 char **av;
344 register int n;
345 char *ap;
346 char *av0;
347 char socknamebuf[2 * MAXSTR];
348 int mflag = 0;
349 char *myname = (ac == 0) ? "screen" : av[0];
350 char *SockDir;
351 struct stat st;
352 #ifdef _MODE_T /* (jw) */
353 mode_t oumask;
354 #else
355 int oumask;
356 #endif
357 #if defined(SYSV) && !defined(ISC)
358 struct utsname utsnam;
359 #endif
360 struct NewWindow nwin;
361 int detached = 0; /* start up detached */
362 #ifdef MULTIUSER
363 char *sockp;
364 #endif
365 char *sty = 0;
367 #if (defined(AUX) || defined(_AUX_SOURCE)) && defined(POSIX)
368 setcompat(COMPAT_POSIX|COMPAT_BSDPROT); /* turn on seteuid support */
369 #endif
370 #if defined(sun) && defined(SVR4)
372 /* Solaris' login blocks SIGHUP! This is _very bad_ */
373 sigset_t sset;
374 sigemptyset(&sset);
375 sigprocmask(SIG_SETMASK, &sset, 0);
377 #endif
380 * First, close all unused descriptors
381 * (otherwise, we might have problems with the select() call)
383 closeallfiles(0);
384 #ifdef DEBUG
385 opendebug(1, 0);
386 #endif
387 sprintf(version, "%d.%.2d.%.2d%s (%s) %s", REV, VERS,
388 PATCHLEVEL, STATE, ORIGIN, DATE);
389 nversion = REV * 10000 + VERS * 100 + PATCHLEVEL;
390 debug2("-- screen debug started %s (%s)\n", *av, version);
391 #ifdef POSIX
392 debug("POSIX\n");
393 #endif
394 #ifdef TERMIO
395 debug("TERMIO\n");
396 #endif
397 #ifdef SYSV
398 debug("SYSV\n");
399 #endif
400 #ifdef SYSVSIGS
401 debug("SYSVSIGS\n");
402 #endif
403 #ifdef NAMEDPIPE
404 debug("NAMEDPIPE\n");
405 #endif
406 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
407 debug("Window size changing enabled\n");
408 #endif
409 #ifdef HAVE_SETREUID
410 debug("SETREUID\n");
411 #endif
412 #ifdef HAVE_SETEUID
413 debug("SETEUID\n");
414 #endif
415 #ifdef hpux
416 debug("hpux\n");
417 #endif
418 #ifdef USEBCOPY
419 debug("USEBCOPY\n");
420 #endif
421 #ifdef UTMPOK
422 debug("UTMPOK\n");
423 #endif
424 #ifdef LOADAV
425 debug("LOADAV\n");
426 #endif
427 #ifdef NETHACK
428 debug("NETHACK\n");
429 #endif
430 #ifdef TERMINFO
431 debug("TERMINFO\n");
432 #endif
433 #ifdef SHADOWPW
434 debug("SHADOWPW\n");
435 #endif
436 #ifdef NAME_MAX
437 debug1("NAME_MAX = %d\n", NAME_MAX);
438 #endif
440 BellString = SaveStr("Bell in window %n");
441 VisualBellString = SaveStr(" Wuff, Wuff!! ");
442 ActivityString = SaveStr("Activity in window %n");
443 screenlogfile = SaveStr("screenlog.%n");
444 logtstamp_string = SaveStr("-- %n:%t -- time-stamp -- %M/%d/%y %c:%s --\n");
445 hstatusstring = SaveStr("%h");
446 captionstring = SaveStr("%3n %t");
447 timestring = SaveStr("%c:%s %M %d %H%? %l%?");
448 wlisttit = SaveStr("Num Name%=Flags");
449 wliststr = SaveStr("%3n %t%=%f");
450 #ifdef COPY_PASTE
451 BufferFile = SaveStr(DEFAULT_BUFFERFILE);
452 #endif
453 ShellProg = NULL;
454 #ifdef POW_DETACH
455 PowDetachString = 0;
456 #endif
457 default_startup = (ac > 1) ? 0 : 1;
458 adaptflag = 0;
459 VBellWait = VBELLWAIT * 1000;
460 MsgWait = MSGWAIT * 1000;
461 MsgMinWait = MSGMINWAIT * 1000;
462 SilenceWait = SILENCEWAIT;
463 #ifdef HAVE_BRAILLE
464 InitBraille();
465 #endif
466 #ifdef ZMODEM
467 zmodem_sendcmd = SaveStr("!!! sz -vv -b ");
468 zmodem_recvcmd = SaveStr("!!! rz -vv -b -E");
469 #endif
471 #ifdef COPY_PASTE
472 CompileKeys((char *)0, 0, mark_key_tab);
473 #endif
474 #ifdef UTF8
475 InitBuiltinTabs();
476 screenencodings = SaveStr(SCREENENCODINGS);
477 #endif
478 #ifdef DW_CHARS
479 cjkwidth = 0;
480 #endif
481 nwin = nwin_undef;
482 nwin_options = nwin_undef;
483 strcpy(screenterm, "screen");
485 logreopen_register(lf_secreopen);
487 av0 = *av;
488 /* if this is a login screen, assume -RR */
489 if (*av0 == '-')
491 rflag = 4;
492 #ifdef MULTI
493 xflag = 1;
494 #else
495 dflag = 1;
496 #endif
497 ShellProg = SaveStr(DefaultShell); /* to prevent nasty circles */
499 while (ac > 0)
501 ap = *++av;
502 if (--ac > 0 && *ap == '-')
504 if (ap[1] == '-' && ap[2] == 0)
506 av++;
507 ac--;
508 break;
510 if (ap[1] == '-' && !strcmp(ap, "--version"))
511 Panic(0, "Screen version %s", version);
512 if (ap[1] == '-' && !strcmp(ap, "--help"))
513 exit_with_usage(myname, NULL, NULL);
514 while (ap && *ap && *++ap)
516 switch (*ap)
518 case 'a':
519 nwin_options.aflag = 1;
520 break;
521 case 'A':
522 adaptflag = 1;
523 break;
524 case 'p': /* preselect */
525 if (*++ap)
526 preselect = ap;
527 else
529 if (!--ac)
530 exit_with_usage(myname, "Specify a window to preselect with -p", NULL);
531 preselect = *++av;
533 ap = NULL;
534 break;
535 #ifdef HAVE_BRAILLE
536 case 'B':
537 bd.bd_start_braille = 1;
538 break;
539 #endif
540 case 'c':
541 if (*++ap)
542 RcFileName = ap;
543 else
545 if (--ac == 0)
546 exit_with_usage(myname, "Specify an alternate rc-filename with -c", NULL);
547 RcFileName = *++av;
549 ap = NULL;
550 break;
551 case 'e':
552 if (!*++ap)
554 if (--ac == 0)
555 exit_with_usage(myname, "Specify command characters with -e", NULL);
556 ap = *++av;
558 if (ParseEscape(ap))
559 Panic(0, "Two characters are required with -e option, not '%s'.", ap);
560 ap = NULL;
561 break;
562 case 'f':
563 ap++;
564 switch (*ap++)
566 case 'n':
567 case '0':
568 nwin_options.flowflag = FLOW_NOW * 0;
569 break;
570 case '\0':
571 ap--;
572 /* FALLTHROUGH */
573 case 'y':
574 case '1':
575 nwin_options.flowflag = FLOW_NOW * 1;
576 break;
577 case 'a':
578 nwin_options.flowflag = FLOW_AUTOFLAG;
579 break;
580 default:
581 exit_with_usage(myname, "Unknown flow option -%s", --ap);
583 break;
584 case 'h':
585 if (--ac == 0)
586 exit_with_usage(myname, NULL, NULL);
587 nwin_options.histheight = atoi(*++av);
588 if (nwin_options.histheight < 0)
589 exit_with_usage(myname, "-h: %s: negative scrollback size?", *av);
590 break;
591 case 'i':
592 iflag = 1;
593 break;
594 case 't': /* title, the former AkA == -k */
595 if (--ac == 0)
596 exit_with_usage(myname, "Specify a new window-name with -t", NULL);
597 nwin_options.aka = *++av;
598 break;
599 case 'l':
600 ap++;
601 switch (*ap++)
603 case 'n':
604 case '0':
605 nwin_options.lflag = 0;
606 break;
607 case '\0':
608 ap--;
609 /* FALLTHROUGH */
610 case 'y':
611 case '1':
612 nwin_options.lflag = 1;
613 break;
614 case 'a':
615 nwin_options.lflag = 3;
616 break;
617 case 's': /* -ls */
618 case 'i': /* -list */
619 lsflag = 1;
620 if (ac > 1 && !SockMatch)
622 SockMatch = *++av;
623 ac--;
625 ap = NULL;
626 break;
627 default:
628 exit_with_usage(myname, "%s: Unknown suboption to -l", --ap);
630 break;
631 case 'w':
632 lsflag = 1;
633 wipeflag = 1;
634 if (ac > 1 && !SockMatch)
636 SockMatch = *++av;
637 ac--;
639 break;
640 case 'L':
641 nwin_options.Lflag = 1;
642 break;
643 case 'm':
644 mflag = 1;
645 break;
646 case 'O': /* to be (or not to be?) deleted. jw. */
647 force_vt = 0;
648 break;
649 case 'T':
650 if (--ac == 0)
651 exit_with_usage(myname, "Specify terminal-type with -T", NULL);
652 if (strlen(*++av) < 20)
653 strcpy(screenterm, *av);
654 else
655 Panic(0, "-T: terminal name too long. (max. 20 char)");
656 nwin_options.term = screenterm;
657 break;
658 case 'q':
659 quietflag = 1;
660 break;
661 case 'r':
662 case 'R':
663 #ifdef MULTI
664 case 'x':
665 #endif
666 if (ac > 1 && *av[1] != '-' && !SockMatch)
668 SockMatch = *++av;
669 ac--;
670 debug2("rflag=%d, SockMatch=%s\n", dflag, SockMatch);
672 #ifdef MULTI
673 if (*ap == 'x')
674 xflag = 1;
675 #endif
676 if (rflag)
677 rflag = 2;
678 rflag += (*ap == 'R') ? 2 : 1;
679 break;
680 #ifdef REMOTE_DETACH
681 case 'd':
682 dflag = 1;
683 /* FALLTHROUGH */
684 case 'D':
685 if (!dflag)
686 dflag = 2;
687 if (ac == 2)
689 if (*av[1] != '-' && !SockMatch)
691 SockMatch = *++av;
692 ac--;
693 debug2("dflag=%d, SockMatch=%s\n", dflag, SockMatch);
696 break;
697 #endif
698 case 's':
699 if (--ac == 0)
700 exit_with_usage(myname, "Specify shell with -s", NULL);
701 if (ShellProg)
702 free(ShellProg);
703 ShellProg = SaveStr(*++av);
704 debug1("ShellProg: '%s'\n", ShellProg);
705 break;
706 case 'S':
707 if (!SockMatch)
709 if (--ac == 0)
710 exit_with_usage(myname, "Specify session-name with -S", NULL);
711 SockMatch = *++av;
713 if (!*SockMatch)
714 exit_with_usage(myname, "Empty session-name?", NULL);
715 break;
716 case 'X':
717 cmdflag = 1;
718 break;
719 case 'v':
720 Panic(0, "Screen version %s", version);
721 /* NOTREACHED */
722 #ifdef UTF8
723 case 'U':
724 nwin_options.encoding = nwin_options.encoding == -1 ? UTF8 : 0;
725 break;
726 #endif
727 default:
728 exit_with_usage(myname, "Unknown option %s", --ap);
732 else
733 break;
736 real_uid = getuid();
737 real_gid = getgid();
738 eff_uid = geteuid();
739 eff_gid = getegid();
740 if (eff_uid != real_uid)
742 /* if running with s-bit, we must install a special signal
743 * handler routine that resets the s-bit, so that we get a
744 * core file anyway.
746 #ifdef SIGBUS /* OOPS, linux has no bus errors! */
747 signal(SIGBUS, CoreDump);
748 #endif /* SIGBUS */
749 signal(SIGSEGV, CoreDump);
752 #ifdef USE_LOCALE
753 setlocale(LC_ALL, "");
754 #endif
755 #ifdef ENCODINGS
756 if (nwin_options.encoding == -1)
758 /* ask locale if we should start in UTF-8 mode */
759 # ifdef HAVE_NL_LANGINFO
760 # ifndef USE_LOCALE
761 setlocale(LC_CTYPE, "");
762 # endif
763 nwin_options.encoding = FindEncoding(nl_langinfo(CODESET));
764 debug1("locale says encoding = %d\n", nwin_options.encoding);
765 # else
766 # ifdef UTF8
767 char *s;
768 if (((s = getenv("LC_ALL")) || (s = getenv("LC_CTYPE")) ||
769 (s = getenv("LANG"))) && InStr(s, "UTF-8"))
770 nwin_options.encoding = UTF8;
771 # endif
772 debug1("environment says encoding=%d\n", nwin_options.encoding);
773 #endif
775 # ifdef DW_CHARS
777 char *s;
778 if((s = getenv("LC_ALL")) || (s = getenv("LC_CTYPE")) ||
779 (s = getenv("LANG")))
781 if(!strncmp(s, "zh_", 3) || !strncmp(s, "ja_", 3) || !strncmp(s, "ko_", 3))
783 cjkwidth = 1;
787 #endif
788 #endif
789 if (SockMatch && strlen(SockMatch) >= MAXSTR)
790 Panic(0, "Ridiculously long socketname - try again.");
791 if (cmdflag && !rflag && !dflag && !xflag)
792 xflag = 1;
793 if (!cmdflag && dflag && mflag && !(rflag || xflag))
794 detached = 1;
795 nwin = nwin_options;
796 #ifdef ENCODINGS
797 nwin.encoding = nwin_undef.encoding; /* let screenrc overwrite it */
798 #endif
799 if (ac)
800 nwin.args = av;
802 /* make the write() calls return -1 on all errors */
803 #ifdef SIGXFSZ
805 * Ronald F. Guilmette, Oct 29 '94, bug-gnu-utils@prep.ai.mit.edu:
806 * It appears that in System V Release 4, UNIX, if you are writing
807 * an output file and you exceed the currently set file size limit,
808 * you _don't_ just get the call to `write' returning with a
809 * failure code. Rather, you get a signal called `SIGXFSZ' which,
810 * if neither handled nor ignored, will cause your program to crash
811 * with a core dump.
813 signal(SIGXFSZ, SIG_IGN);
814 #endif /* SIGXFSZ */
816 #ifdef SIGPIPE
817 signal(SIGPIPE, SIG_IGN);
818 #endif
820 if (!ShellProg)
822 register char *sh;
824 sh = getenv("SHELL");
825 ShellProg = SaveStr(sh ? sh : DefaultShell);
827 ShellArgs[0] = ShellProg;
828 home = getenv("HOME");
829 if (!mflag && !SockMatch)
831 sty = getenv("STY");
832 if (sty && *sty == 0)
833 sty = 0;
836 #ifdef NETHACK
837 if (!(nethackflag = (getenv("NETHACKOPTIONS") != NULL)))
839 char nethackrc[MAXPATHLEN];
841 if (home && (strlen(home) < (MAXPATHLEN - 20)))
843 sprintf(nethackrc,"%s/.nethackrc", home);
844 nethackflag = !access(nethackrc, F_OK);
847 #endif
849 #ifdef MULTIUSER
850 own_uid = multi_uid = real_uid;
851 if (SockMatch && (sockp = index(SockMatch, '/')))
853 if (eff_uid)
854 Panic(0, "Must run suid root for multiuser support.");
855 *sockp = 0;
856 multi = SockMatch;
857 SockMatch = sockp + 1;
858 if (*multi)
860 struct passwd *mppp;
861 if ((mppp = getpwnam(multi)) == (struct passwd *)0)
862 Panic(0, "Cannot identify account '%s'.", multi);
863 multi_uid = mppp->pw_uid;
864 multi_home = SaveStr(mppp->pw_dir);
865 if (strlen(multi_home) > MAXPATHLEN - 10)
866 Panic(0, "home directory path too long");
867 # ifdef MULTI
868 /* always fake multi attach mode */
869 if (rflag || lsflag)
870 xflag = 1;
871 # endif /* MULTI */
872 detached = 0;
873 multiattach = 1;
876 if (SockMatch && *SockMatch == 0)
877 SockMatch = 0;
878 #endif /* MULTIUSER */
880 if ((LoginName = getlogin()) && LoginName[0] != '\0')
882 if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0)
883 if ((int)ppp->pw_uid != real_uid)
884 ppp = (struct passwd *) 0;
886 if (ppp == 0)
888 if ((ppp = getpwuid(real_uid)) == 0)
890 Panic(0, "getpwuid() can't identify your account!");
891 exit(1);
893 LoginName = ppp->pw_name;
895 LoginName = SaveStr(LoginName);
897 ppp = getpwbyname(LoginName, ppp);
899 #if !defined(SOCKDIR) && defined(MULTIUSER)
900 if (multi && !multiattach)
902 if (home && strcmp(home, ppp->pw_dir))
903 Panic(0, "$HOME must match passwd entry for multiuser screens.");
905 #endif
907 #define SET_GUID() do \
909 setgid(real_gid); \
910 setuid(real_uid); \
911 eff_uid = real_uid; \
912 eff_gid = real_gid; \
913 } while (0)
915 #define SET_TTYNAME(fatal) do \
917 if (!(attach_tty = ttyname(0))) \
919 if (fatal) \
920 Panic(0, "Must be connected to a terminal."); \
921 else \
922 attach_tty = ""; \
924 else if (stat(attach_tty, &st)) \
925 Panic(errno, "Cannot access '%s'", attach_tty); \
926 if (strlen(attach_tty) >= MAXPATHLEN) \
927 Panic(0, "TtyName too long - sorry."); \
928 } while (0)
930 if (home == 0 || *home == '\0')
931 home = ppp->pw_dir;
932 if (strlen(LoginName) > 20)
933 Panic(0, "LoginName too long - sorry.");
934 #ifdef MULTIUSER
935 if (multi && strlen(multi) > 20)
936 Panic(0, "Screen owner name too long - sorry.");
937 #endif
938 if (strlen(home) > MAXPATHLEN - 25)
939 Panic(0, "$HOME too long - sorry.");
941 attach_tty = "";
942 if (!detached && !lsflag && !cmdflag && !(dflag && !mflag && !rflag && !xflag) && !(!mflag && !SockMatch && sty))
944 #ifndef NAMEDPIPE
945 int fl;
946 #endif
948 /* ttyname implies isatty */
949 SET_TTYNAME(1);
950 #ifdef MULTIUSER
951 tty_mode = (int)st.st_mode & 0777;
952 #endif
954 #ifndef NAMEDPIPE
955 fl = fcntl(0, F_GETFL, 0);
956 if (fl != -1 && (fl & (O_RDWR|O_RDONLY|O_WRONLY)) == O_RDWR)
957 attach_fd = 0;
958 #endif
959 if (attach_fd == -1)
961 if ((n = secopen(attach_tty, O_RDWR | O_NONBLOCK, 0)) < 0)
962 Panic(0, "Cannot open your terminal '%s' - please check.", attach_tty);
963 close(n);
965 debug2("attach_tty is %s, attach_fd is %d\n", attach_tty, attach_fd);
967 if ((attach_term = getenv("TERM")) == 0 || *attach_term == 0)
968 Panic(0, "Please set a terminal type.");
969 if (strlen(attach_term) > sizeof(D_termname) - 1)
970 Panic(0, "$TERM too long - sorry.");
971 GetTTY(0, &attach_Mode);
972 #ifdef DEBUGGGGGGGGGGGGGGG
973 DebugTTY(&attach_Mode);
974 #endif /* DEBUG */
977 #ifdef _MODE_T
978 oumask = umask(0); /* well, unsigned never fails? jw. */
979 #else
980 if ((oumask = (int)umask(0)) == -1)
981 Panic(errno, "Cannot change umask to zero");
982 #endif
983 SockDir = getenv("SCREENDIR");
984 if (SockDir)
986 if (strlen(SockDir) >= MAXPATHLEN - 1)
987 Panic(0, "Ridiculously long $SCREENDIR - try again.");
988 #ifdef MULTIUSER
989 if (multi)
990 Panic(0, "No $SCREENDIR with multi screens, please.");
991 #endif
993 #ifdef MULTIUSER
994 if (multiattach)
996 # ifndef SOCKDIR
997 sprintf(SockPath, "%s/.screen", multi_home);
998 SockDir = SockPath;
999 # else
1000 SockDir = SOCKDIR;
1001 sprintf(SockPath, "%s/S-%s", SockDir, multi);
1002 # endif
1004 else
1005 #endif
1007 #ifndef SOCKDIR
1008 if (SockDir == 0)
1010 sprintf(SockPath, "%s/.screen", home);
1011 SockDir = SockPath;
1013 #endif
1014 if (SockDir)
1016 if (access(SockDir, F_OK))
1018 debug1("SockDir '%s' missing ...\n", SockDir);
1019 if (UserContext() > 0)
1021 if (mkdir(SockDir, 0700))
1022 UserReturn(0);
1023 UserReturn(1);
1025 if (UserStatus() <= 0)
1026 Panic(0, "Cannot make directory '%s'.", SockDir);
1028 if (SockDir != SockPath)
1029 strcpy(SockPath, SockDir);
1031 #ifdef SOCKDIR
1032 else
1034 SockDir = SOCKDIR;
1035 if (lstat(SockDir, &st))
1037 n = (eff_uid == 0 && (real_uid || eff_gid == real_gid)) ? 0755 :
1038 (eff_gid != real_gid) ? 0775 :
1039 #ifdef S_ISVTX
1040 0777|S_ISVTX;
1041 #else
1042 0777;
1043 #endif
1044 if (mkdir(SockDir, n) == -1)
1045 Panic(errno, "Cannot make directory '%s'", SockDir);
1047 else
1049 if (!S_ISDIR(st.st_mode))
1050 Panic(0, "'%s' must be a directory.", SockDir);
1051 if (eff_uid == 0 && real_uid && (int)st.st_uid != eff_uid)
1052 Panic(0, "Directory '%s' must be owned by root.", SockDir);
1053 n = (eff_uid == 0 && (real_uid || (st.st_mode & 0775) != 0775)) ? 0755 :
1054 (eff_gid == (int)st.st_gid && eff_gid != real_gid) ? 0775 :
1055 0777;
1056 if (((int)st.st_mode & 0777) != n)
1057 Panic(0, "Directory '%s' must have mode %03o.", SockDir, n);
1059 sprintf(SockPath, "%s/S-%s", SockDir, LoginName);
1060 if (access(SockPath, F_OK))
1062 if (mkdir(SockPath, 0700) == -1)
1063 Panic(errno, "Cannot make directory '%s'", SockPath);
1064 (void) chown(SockPath, real_uid, real_gid);
1067 #endif
1070 if (stat(SockPath, &st) == -1)
1071 Panic(errno, "Cannot access %s", SockPath);
1072 else
1073 if (!S_ISDIR(st.st_mode))
1074 Panic(0, "%s is not a directory.", SockPath);
1075 #ifdef MULTIUSER
1076 if (multi)
1078 if ((int)st.st_uid != multi_uid)
1079 Panic(0, "%s is not the owner of %s.", multi, SockPath);
1081 else
1082 #endif
1084 if ((int)st.st_uid != real_uid)
1085 Panic(0, "You are not the owner of %s.", SockPath);
1087 if ((st.st_mode & 0777) != 0700)
1088 Panic(0, "Directory %s must have mode 700.", SockPath);
1089 if (SockMatch && index(SockMatch, '/'))
1090 Panic(0, "Bad session name '%s'", SockMatch);
1091 SockName = SockPath + strlen(SockPath) + 1;
1092 *SockName = 0;
1093 (void) umask(oumask);
1094 debug2("SockPath: %s SockMatch: %s\n", SockPath, SockMatch ? SockMatch : "NULL");
1096 #if defined(SYSV) && !defined(ISC)
1097 if (uname(&utsnam) == -1)
1098 Panic(errno, "uname");
1099 strncpy(HostName, utsnam.nodename, sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1);
1100 HostName[sizeof(utsnam.nodename) < MAXSTR ? sizeof(utsnam.nodename) : MAXSTR - 1] = '\0';
1101 #else
1102 (void) gethostname(HostName, MAXSTR);
1103 HostName[MAXSTR - 1] = '\0';
1104 #endif
1105 if ((ap = index(HostName, '.')) != NULL)
1106 *ap = '\0';
1108 if (lsflag)
1110 int i, fo, oth;
1112 #ifdef MULTIUSER
1113 if (multi)
1114 real_uid = multi_uid;
1115 #endif
1116 SET_GUID();
1117 i = FindSocket((int *)NULL, &fo, &oth, SockMatch);
1118 if (quietflag)
1119 exit(8 + (fo ? ((oth || i) ? 2 : 1) : 0) + i);
1120 if (fo == 0)
1121 Panic(0, "No Sockets found in %s.\n", SockPath);
1122 Panic(0, "%d Socket%s in %s.\n", fo, fo > 1 ? "s" : "", SockPath);
1123 /* NOTREACHED */
1125 signal(SIG_BYE, AttacherFinit); /* prevent races */
1126 if (cmdflag)
1128 /* attach_tty is not mandatory */
1129 SET_TTYNAME(0);
1130 if (!*av)
1131 Panic(0, "Please specify a command.");
1132 SET_GUID();
1133 SendCmdMessage(sty, SockMatch, av);
1134 exit(0);
1136 else if (rflag || xflag)
1138 debug("screen -r: - is there anybody out there?\n");
1139 if (Attach(MSG_ATTACH))
1141 Attacher();
1142 /* NOTREACHED */
1144 #ifdef MULTIUSER
1145 if (multiattach)
1146 Panic(0, "Can't create sessions of other users.");
1147 #endif
1148 debug("screen -r: backend not responding -- still crying\n");
1150 else if (dflag && !mflag)
1152 (void) Attach(MSG_DETACH);
1153 Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : ""));
1154 eexit(0);
1155 /* NOTREACHED */
1157 if (!SockMatch && !mflag && sty)
1159 /* attach_tty is not mandatory */
1160 SET_TTYNAME(0);
1161 SET_GUID();
1162 nwin_options.args = av;
1163 SendCreateMsg(sty, &nwin);
1164 exit(0);
1165 /* NOTREACHED */
1167 nwin_compose(&nwin_default, &nwin_options, &nwin_default);
1169 if (!detached || dflag != 2)
1170 MasterPid = fork();
1171 else
1172 MasterPid = 0;
1174 switch (MasterPid)
1176 case -1:
1177 Panic(errno, "fork");
1178 /* NOTREACHED */
1179 case 0:
1180 break;
1181 default:
1182 if (detached)
1183 exit(0);
1184 if (SockMatch)
1185 sprintf(socknamebuf, "%d.%s", MasterPid, SockMatch);
1186 else
1187 sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty), HostName);
1188 for (ap = socknamebuf; *ap; ap++)
1189 if (*ap == '/')
1190 *ap = '-';
1191 #ifdef NAME_MAX
1192 if (strlen(socknamebuf) > NAME_MAX)
1193 socknamebuf[NAME_MAX] = 0;
1194 #endif
1195 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1196 SET_GUID();
1197 Attacher();
1198 /* NOTREACHED */
1201 if (!detached)
1202 PanicPid = getppid();
1204 if (DefaultEsc == -1)
1205 DefaultEsc = Ctrl('a');
1206 if (DefaultMetaEsc == -1)
1207 DefaultMetaEsc = 'a';
1209 ap = av0 + strlen(av0) - 1;
1210 while (ap >= av0)
1212 if (!strncmp("screen", ap, 6))
1214 strncpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
1215 break;
1217 ap--;
1219 if (ap < av0)
1220 *av0 = 'S';
1222 #ifdef DEBUG
1224 char buf[256];
1226 if (dfp && dfp != stderr)
1227 fclose(dfp);
1228 sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, (int)getpid());
1229 if ((dfp = fopen(buf, "w")) == NULL)
1230 dfp = stderr;
1231 else
1232 (void) chmod(buf, 0666);
1234 #endif
1235 if (!detached)
1237 if (attach_fd == -1)
1239 if ((n = secopen(attach_tty, O_RDWR, 0)) < 0)
1240 Panic(0, "Cannot reopen '%s' - please check.", attach_tty);
1242 else
1243 n = dup(attach_fd);
1245 else
1246 n = -1;
1247 freopen("/dev/null", "r", stdin);
1248 freopen("/dev/null", "w", stdout);
1250 #ifdef DEBUG
1251 if (dfp != stderr)
1252 #endif
1253 freopen("/dev/null", "w", stderr);
1254 debug("-- screen.back debug started\n");
1257 * This guarantees that the session owner is listed, even when we
1258 * start detached. From now on we should not refer to 'LoginName'
1259 * any more, use users->u_name instead.
1261 if (UserAdd(LoginName, (char *)0, (struct acluser **)0) < 0)
1262 Panic(0, "Could not create user info");
1263 if (!detached)
1265 if (MakeDisplay(LoginName, attach_tty, attach_term, n, getppid(), &attach_Mode) == 0)
1266 Panic(0, "Could not alloc display");
1267 PanicPid = 0;
1268 #ifdef ENCODINGS
1269 D_encoding = nwin_options.encoding > 0 ? nwin_options.encoding : 0;
1270 debug1("D_encoding = %d\n", D_encoding);
1271 #endif
1274 if (SockMatch)
1276 /* user started us with -S option */
1277 sprintf(socknamebuf, "%d.%s", (int)getpid(), SockMatch);
1279 else
1281 sprintf(socknamebuf, "%d.%s.%s", (int)getpid(), stripdev(attach_tty),
1282 HostName);
1284 for (ap = socknamebuf; *ap; ap++)
1285 if (*ap == '/')
1286 *ap = '-';
1287 #ifdef NAME_MAX
1288 if (strlen(socknamebuf) > NAME_MAX)
1290 debug2("Socketname %s truncated to %d chars\n", socknamebuf, NAME_MAX);
1291 socknamebuf[NAME_MAX] = 0;
1293 #endif
1294 sprintf(SockPath + strlen(SockPath), "/%s", socknamebuf);
1296 ServerSocket = MakeServerSocket();
1297 InitKeytab();
1298 #ifdef ETCSCREENRC
1299 # ifdef ALLOW_SYSSCREENRC
1300 if ((ap = getenv("SYSSCREENRC")))
1301 (void)StartRc(ap, 0);
1302 else
1303 # endif
1304 (void)StartRc(ETCSCREENRC, 0);
1305 #endif
1306 (void)StartRc(RcFileName, 0);
1307 # ifdef UTMPOK
1308 # ifndef UTNOKEEP
1309 InitUtmp();
1310 # endif /* UTNOKEEP */
1311 # endif /* UTMPOK */
1312 if (display)
1314 if (InitTermcap(0, 0))
1316 debug("Could not init termcap - exiting\n");
1317 fcntl(D_userfd, F_SETFL, 0); /* Flush sets FNBLOCK */
1318 freetty();
1319 if (D_userpid)
1320 Kill(D_userpid, SIG_BYE);
1321 eexit(1);
1323 MakeDefaultCanvas();
1324 InitTerm(0);
1325 #ifdef UTMPOK
1326 RemoveLoginSlot();
1327 #endif
1329 else
1330 MakeTermcap(1);
1331 #ifdef LOADAV
1332 InitLoadav();
1333 #endif /* LOADAV */
1334 MakeNewEnv();
1335 signal(SIGHUP, SigHup);
1336 signal(SIGINT, FinitHandler);
1337 signal(SIGQUIT, FinitHandler);
1338 signal(SIGTERM, FinitHandler);
1339 #ifdef BSDJOBS
1340 signal(SIGTTIN, SIG_IGN);
1341 signal(SIGTTOU, SIG_IGN);
1342 #endif
1344 if (display)
1346 brktty(D_userfd);
1347 SetMode(&D_OldMode, &D_NewMode, D_flow, iflag);
1348 /* Note: SetMode must be called _before_ FinishRc. */
1349 SetTTY(D_userfd, &D_NewMode);
1350 if (fcntl(D_userfd, F_SETFL, FNBLOCK))
1351 Msg(errno, "Warning: NBLOCK fcntl failed");
1353 else
1354 brktty(-1); /* just try */
1355 signal(SIGCHLD, SigChld);
1356 #ifdef ETCSCREENRC
1357 # ifdef ALLOW_SYSSCREENRC
1358 if ((ap = getenv("SYSSCREENRC")))
1359 FinishRc(ap);
1360 else
1361 # endif
1362 FinishRc(ETCSCREENRC);
1363 #endif
1364 FinishRc(RcFileName);
1366 debug2("UID %d EUID %d\n", (int)getuid(), (int)geteuid());
1367 if (windows == NULL)
1369 debug("We open one default window, as screenrc did not specify one.\n");
1370 if (MakeWindow(&nwin) == -1)
1372 Msg(0, "Sorry, could not find a PTY.");
1373 sleep(5);
1374 Finit(0);
1375 /* NOTREACHED */
1379 #ifdef HAVE_BRAILLE
1380 StartBraille();
1381 #endif
1383 if (display && default_startup)
1384 display_copyright();
1385 signal(SIGINT, SigInt);
1386 if (rflag && (rflag & 1) == 0 && !quietflag)
1388 Msg(0, "New screen...");
1389 rflag = 0;
1392 serv_read.type = EV_READ;
1393 serv_read.fd = ServerSocket;
1394 serv_read.handler = serv_read_fn;
1395 evenq(&serv_read);
1397 serv_select.pri = -10;
1398 serv_select.type = EV_ALWAYS;
1399 serv_select.handler = serv_select_fn;
1400 evenq(&serv_select);
1402 logflushev.type = EV_TIMEOUT;
1403 logflushev.handler = logflush_fn;
1405 sched();
1406 /* NOTREACHED */
1407 return 0;
1410 void
1411 WindowDied(p, wstat, wstat_valid)
1412 struct win *p;
1413 int wstat;
1414 int wstat_valid;
1416 int killit = 0;
1418 if (ZombieKey_destroy && ZombieKey_onerror && wstat_valid &&
1419 WIFEXITED(wstat) && WEXITSTATUS(wstat) == 0)
1420 killit = 1;
1422 if (ZombieKey_destroy && !killit)
1424 char buf[100], *s, reason[100];
1425 time_t now;
1427 if (wstat_valid) {
1428 if (WIFEXITED(wstat))
1429 if (WEXITSTATUS(wstat))
1430 sprintf(reason, "terminated with exit status %d", WEXITSTATUS(wstat));
1431 else
1432 sprintf(reason, "terminated normally");
1433 else if (WIFSIGNALED(wstat))
1434 sprintf(reason, "terminated with signal %d%s", WTERMSIG(wstat),
1435 #ifdef WCOREDUMP
1436 WCOREDUMP(wstat) ? " (core file generated)" : "");
1437 #else
1438 "");
1439 #endif
1440 } else
1441 sprintf(reason, "detached from window");
1443 (void) time(&now);
1444 s = ctime(&now);
1445 if (s && *s)
1446 s[strlen(s) - 1] = '\0';
1447 debug3("window %d (%s) going into zombie state fd %d",
1448 p->w_number, p->w_title, p->w_ptyfd);
1449 #ifdef UTMPOK
1450 if (p->w_slot != (slot_t)0 && p->w_slot != (slot_t)-1)
1452 RemoveUtmp(p);
1453 p->w_slot = 0; /* "detached" */
1455 #endif
1456 CloseDevice(p);
1458 p->w_deadpid = p->w_pid;
1459 p->w_pid = 0;
1460 ResetWindow(p);
1461 /* p->w_y = p->w_bot; */
1462 p->w_y = MFindUsedLine(p, p->w_bot, 1);
1463 sprintf(buf, "\n\r=== Command %s (%s) ===", reason, s ? s : "?");
1464 WriteString(p, buf, strlen(buf));
1465 WindowChanged(p, 'f');
1467 else
1468 KillWindow(p);
1469 #ifdef UTMPOK
1470 CarefulUtmp();
1471 #endif
1474 static void
1475 SigChldHandler()
1477 struct stat st;
1478 #ifdef DEBUG
1479 fds();
1480 #endif
1481 while (GotSigChld)
1483 GotSigChld = 0;
1484 DoWait();
1485 #ifdef SYSVSIGS
1486 signal(SIGCHLD, SigChld);
1487 #endif
1489 if (stat(SockPath, &st) == -1)
1491 debug1("SigChldHandler: Yuck! cannot stat '%s'\n", SockPath);
1492 if (!RecoverSocket())
1494 debug("SCREEN cannot recover from corrupt Socket, bye\n");
1495 Finit(1);
1497 else
1498 debug1("'%s' reconstructed\n", SockPath);
1500 else
1501 debug2("SigChldHandler: stat '%s' o.k. (%03o)\n", SockPath, (int)st.st_mode);
1504 static sigret_t
1505 SigChld SIGDEFARG
1507 debug("SigChld()\n");
1508 GotSigChld = 1;
1509 SIGRETURN;
1512 sigret_t
1513 SigHup SIGDEFARG
1515 /* Hangup all displays */
1516 while ((display = displays) != 0)
1517 Hangup();
1518 SIGRETURN;
1522 * the backend's Interrupt handler
1523 * we cannot insert the intrc directly, as we never know
1524 * if fore is valid.
1526 static sigret_t
1527 SigInt SIGDEFARG
1529 #if HAZARDOUS
1530 char ibuf;
1532 debug("SigInt()\n");
1533 if (fore && displays)
1535 # if defined(TERMIO) || defined(POSIX)
1536 ibuf = displays->d_OldMode.tio.c_cc[VINTR];
1537 # else
1538 ibuf = displays->d_OldMode.m_tchars.t_intrc;
1539 # endif
1540 fore->w_inlen = 0;
1541 write(fore->w_ptyfd, &ibuf, 1);
1543 #else
1544 signal(SIGINT, SigInt);
1545 debug("SigInt() careful\n");
1546 InterruptPlease = 1;
1547 #endif
1548 SIGRETURN;
1551 static sigret_t
1552 CoreDump SIGDEFARG
1554 struct display *disp;
1555 char buf[80];
1557 #if defined(SYSVSIGS) && defined(SIGHASARG)
1558 signal(sigsig, SIG_IGN);
1559 #endif
1560 setgid(getgid());
1561 setuid(getuid());
1562 unlink("core");
1563 #ifdef SIGHASARG
1564 sprintf(buf, "\r\n[screen caught signal %d.%s]\r\n", sigsig,
1565 #else
1566 sprintf(buf, "\r\n[screen caught a fatal signal.%s]\r\n",
1567 #endif
1568 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1570 #else /* SHADOWPW && !DEBUG */
1571 " (core dumped)"
1572 #endif /* SHADOWPW && !DEBUG */
1574 for (disp = displays; disp; disp = disp->d_next)
1576 fcntl(disp->d_userfd, F_SETFL, 0);
1577 SetTTY(disp->d_userfd, &D_OldMode);
1578 write(disp->d_userfd, buf, strlen(buf));
1579 Kill(disp->d_userpid, SIG_BYE);
1581 #if defined(SHADOWPW) && !defined(DEBUG) && !defined(DUMPSHADOW)
1582 Kill(getpid(), SIGKILL);
1583 eexit(11);
1584 #else /* SHADOWPW && !DEBUG */
1585 abort();
1586 #endif /* SHADOWPW && !DEBUG */
1587 SIGRETURN;
1590 static void
1591 DoWait()
1593 register int pid;
1594 struct win *p, *next;
1595 #ifdef BSDWAIT
1596 union wait wstat;
1597 #else
1598 int wstat;
1599 #endif
1601 #ifdef BSDJOBS
1602 # ifndef BSDWAIT
1603 while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0)
1604 # else
1605 # ifdef USE_WAIT2
1607 * From: rouilj@sni-usa.com (John Rouillard)
1608 * note that WUNTRACED is not documented to work, but it is defined in
1609 * /usr/include/sys/wait.h, so it may work
1611 while ((pid = wait2(&wstat, WNOHANG | WUNTRACED )) > 0)
1612 # else /* USE_WAIT2 */
1613 while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0)
1614 # endif /* USE_WAIT2 */
1615 # endif
1616 #else /* BSDJOBS */
1617 while ((pid = wait(&wstat)) < 0)
1618 if (errno != EINTR)
1619 break;
1620 if (pid > 0)
1621 #endif /* BSDJOBS */
1623 for (p = windows; p; p = next)
1625 next = p->w_next;
1626 if ( (p->w_pid && pid == p->w_pid) ||
1627 (p->w_deadpid && pid == p->w_deadpid) )
1629 /* child has ceased to exist */
1630 p->w_pid = 0;
1632 #ifdef BSDJOBS
1633 if (WIFSTOPPED(wstat))
1635 debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, pid, WSTOPSIG(wstat));
1636 #ifdef SIGTTIN
1637 if (WSTOPSIG(wstat) == SIGTTIN)
1639 Msg(0, "Suspended (tty input)");
1640 continue;
1642 #endif
1643 #ifdef SIGTTOU
1644 if (WSTOPSIG(wstat) == SIGTTOU)
1646 Msg(0, "Suspended (tty output)");
1647 continue;
1649 #endif
1650 /* Try to restart process */
1651 Msg(0, "Child has been stopped, restarting.");
1652 if (killpg(pid, SIGCONT))
1653 kill(pid, SIGCONT);
1655 else
1656 #endif
1658 WindowDied(p, wstat, 1);
1660 break;
1662 #ifdef PSEUDOS
1663 if (p->w_pwin && pid == p->w_pwin->p_pid)
1665 debug2("pseudo of win Nr %d died. pid == %d\n", p->w_number, p->w_pwin->p_pid);
1666 FreePseudowin(p);
1667 break;
1669 #endif
1671 if (p == 0)
1673 debug1("pid %d not found - hope that's ok\n", pid);
1679 static sigret_t
1680 FinitHandler SIGDEFARG
1682 #ifdef SIGHASARG
1683 debug1("FinitHandler called, sig %d.\n", sigsig);
1684 #else
1685 debug("FinitHandler called.\n");
1686 #endif
1687 Finit(1);
1688 SIGRETURN;
1691 void
1692 Finit(i)
1693 int i;
1695 signal(SIGCHLD, SIG_DFL);
1696 signal(SIGHUP, SIG_IGN);
1697 debug1("Finit(%d);\n", i);
1698 while (windows)
1700 struct win *p = windows;
1701 windows = windows->w_next;
1702 FreeWindow(p);
1704 if (ServerSocket != -1)
1706 debug1("we unlink(%s)\n", SockPath);
1707 #ifdef USE_SETEUID
1708 xseteuid(real_uid);
1709 xsetegid(real_gid);
1710 #endif
1711 (void) unlink(SockPath);
1712 #ifdef USE_SETEUID
1713 xseteuid(eff_uid);
1714 xsetegid(eff_gid);
1715 #endif
1717 for (display = displays; display; display = display->d_next)
1719 if (D_status)
1720 RemoveStatus();
1721 FinitTerm();
1722 #ifdef UTMPOK
1723 RestoreLoginSlot();
1724 #endif
1725 AddStr("[screen is terminating]\r\n");
1726 Flush();
1727 SetTTY(D_userfd, &D_OldMode);
1728 fcntl(D_userfd, F_SETFL, 0);
1729 freetty();
1730 Kill(D_userpid, SIG_BYE);
1733 * we _cannot_ call eexit(i) here,
1734 * instead of playing with the Socket above. Sigh.
1736 exit(i);
1739 void
1740 eexit(e)
1741 int e;
1743 debug("eexit\n");
1744 if (ServerSocket != -1)
1746 debug1("we unlink(%s)\n", SockPath);
1747 setgid(real_gid);
1748 setuid(real_uid);
1749 (void) unlink(SockPath);
1751 exit(e);
1754 void
1755 Hangup()
1757 if (display == 0)
1758 return;
1759 debug1("Hangup %x\n", display);
1760 if (D_userfd >= 0)
1762 close(D_userfd);
1763 D_userfd = -1;
1765 if (auto_detach || displays->d_next)
1766 Detach(D_HANGUP);
1767 else
1768 Finit(0);
1772 * Detach now has the following modes:
1773 *D_DETACH SIG_BYE detach backend and exit attacher
1774 *D_HANGUP SIG_BYE detach backend and exit attacher
1775 *D_STOP SIG_STOP stop attacher (and detach backend)
1776 *D_REMOTE SIG_BYE remote detach -- reattach to new attacher
1777 *D_POWER SIG_POWER_BYE power detach -- attacher kills his parent
1778 *D_REMOTE_POWER SIG_POWER_BYE remote power detach -- both
1779 *D_LOCK SIG_LOCK lock the attacher
1780 * (jw)
1781 * we always remove our utmp slots. (even when "lock" or "stop")
1782 * Note: Take extra care here, we may be called by interrupt!
1784 void
1785 Detach(mode)
1786 int mode;
1788 int sign = 0, pid;
1789 struct canvas *cv;
1790 struct win *p;
1792 if (display == 0)
1793 return;
1795 signal(SIGHUP, SIG_IGN);
1796 debug1("Detach(%d)\n", mode);
1797 if (D_status)
1798 RemoveStatus();
1799 FinitTerm();
1800 if (!display)
1801 return;
1802 switch (mode)
1804 case D_HANGUP:
1805 sign = SIG_BYE;
1806 break;
1807 case D_DETACH:
1808 AddStr("[detached]\r\n");
1809 sign = SIG_BYE;
1810 break;
1811 #ifdef BSDJOBS
1812 case D_STOP:
1813 sign = SIG_STOP;
1814 break;
1815 #endif
1816 #ifdef REMOTE_DETACH
1817 case D_REMOTE:
1818 AddStr("[remote detached]\r\n");
1819 sign = SIG_BYE;
1820 break;
1821 #endif
1822 #ifdef POW_DETACH
1823 case D_POWER:
1824 AddStr("[power detached]\r\n");
1825 if (PowDetachString)
1827 AddStr(PowDetachString);
1828 AddStr("\r\n");
1830 sign = SIG_POWER_BYE;
1831 break;
1832 #ifdef REMOTE_DETACH
1833 case D_REMOTE_POWER:
1834 AddStr("[remote power detached]\r\n");
1835 if (PowDetachString)
1837 AddStr(PowDetachString);
1838 AddStr("\r\n");
1840 sign = SIG_POWER_BYE;
1841 break;
1842 #endif
1843 #endif
1844 case D_LOCK:
1845 ClearAll();
1846 sign = SIG_LOCK;
1847 /* tell attacher to lock terminal with a lockprg. */
1848 break;
1850 #ifdef UTMPOK
1851 if (displays->d_next == 0)
1853 for (p = windows; p; p = p->w_next)
1855 if (p->w_slot != (slot_t) -1 && !(p->w_lflag & 2))
1857 RemoveUtmp(p);
1859 * Set the slot to 0 to get the window
1860 * logged in again.
1862 p->w_slot = (slot_t) 0;
1866 if (mode != D_HANGUP)
1867 RestoreLoginSlot();
1868 #endif
1869 if (displays->d_next == 0 && console_window)
1871 if (TtyGrabConsole(console_window->w_ptyfd, 0, "detach"))
1873 debug("could not release console - killing window\n");
1874 KillWindow(console_window);
1875 display = displays; /* restore display */
1878 if (D_fore)
1880 #ifdef MULTIUSER
1881 ReleaseAutoWritelock(display, D_fore);
1882 #endif
1883 D_user->u_detachwin = D_fore->w_number;
1884 D_user->u_detachotherwin = D_other ? D_other->w_number : -1;
1886 AutosaveLayout(D_layout);
1887 layout_last = D_layout;
1888 for (cv = D_cvlist; cv; cv = cv->c_next)
1890 p = Layer2Window(cv->c_layer);
1891 SetCanvasWindow(cv, 0);
1892 if (p)
1893 WindowChanged(p, 'u');
1896 pid = D_userpid;
1897 debug2("display: %#x displays: %#x\n", (unsigned int)display, (unsigned int)displays);
1898 FreeDisplay();
1899 if (displays == 0)
1900 /* Flag detached-ness */
1901 (void) chsock();
1903 * tell father what to do. We do that after we
1904 * freed the tty, thus getty feels more comfortable on hpux
1905 * if it was a power detach.
1907 Kill(pid, sign);
1908 debug2("Detach: Signal %d to Attacher(%d)!\n", sign, pid);
1909 debug("Detach returns, we are successfully detached.\n");
1910 signal(SIGHUP, SigHup);
1913 static int
1914 IsSymbol(e, s)
1915 char *e, *s;
1917 register int l;
1919 l = strlen(s);
1920 return strncmp(e, s, l) == 0 && e[l] == '=';
1923 void
1924 MakeNewEnv()
1926 register char **op, **np;
1927 static char stybuf[MAXSTR];
1929 for (op = environ; *op; ++op)
1931 if (NewEnv)
1932 free((char *)NewEnv);
1933 NewEnv = np = (char **) malloc((unsigned) (op - environ + 7 + 1) * sizeof(char **));
1934 if (!NewEnv)
1935 Panic(0, strnomem);
1936 sprintf(stybuf, "STY=%s", strlen(SockName) <= MAXSTR - 5 ? SockName : "?");
1937 *np++ = stybuf; /* NewEnv[0] */
1938 *np++ = Term; /* NewEnv[1] */
1939 np++; /* room for SHELL */
1940 #ifdef TIOCSWINSZ
1941 np += 2; /* room for TERMCAP and WINDOW */
1942 #else
1943 np += 4; /* room for TERMCAP WINDOW LINES COLUMNS */
1944 #endif
1946 for (op = environ; *op; ++op)
1948 if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP")
1949 && !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW")
1950 && !IsSymbol(*op, "SCREENCAP") && !IsSymbol(*op, "SHELL")
1951 && !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS")
1953 *np++ = *op;
1955 *np = 0;
1958 void
1959 /*VARARGS2*/
1960 #if defined(USEVARARGS) && defined(__STDC__)
1961 Msg(int err, char *fmt, VA_DOTS)
1962 #else
1963 Msg(err, fmt, VA_DOTS)
1964 int err;
1965 char *fmt;
1966 VA_DECL
1967 #endif
1969 VA_LIST(ap)
1970 char buf[MAXPATHLEN*2];
1971 char *p = buf;
1973 VA_START(ap, fmt);
1974 fmt = DoNLS(fmt);
1975 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
1976 VA_END(ap);
1977 if (err)
1979 p += strlen(p);
1980 *p++ = ':';
1981 *p++ = ' ';
1982 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
1983 buf[sizeof(buf) - 1] = 0;
1985 debug2("Msg('%s') (%#x);\n", buf, (unsigned int)display);
1987 if (display && displays)
1988 MakeStatus(buf);
1989 else if (displays)
1991 for (display = displays; display; display = display->d_next)
1992 MakeStatus(buf);
1994 else if (display)
1996 /* no displays but a display - must have forked.
1997 * send message to backend!
1999 char *tty = D_usertty;
2000 struct display *olddisplay = display;
2001 display = 0; /* only send once */
2002 SendErrorMsg(tty, buf);
2003 display = olddisplay;
2005 else
2006 printf("%s\r\n", buf);
2010 * Call FinitTerm for all displays, write a message to each and call eexit();
2012 void
2013 /*VARARGS2*/
2014 #if defined(USEVARARGS) && defined(__STDC__)
2015 Panic(int err, char *fmt, VA_DOTS)
2016 #else
2017 Panic(err, fmt, VA_DOTS)
2018 int err;
2019 char *fmt;
2020 VA_DECL
2021 #endif
2023 VA_LIST(ap)
2024 char buf[MAXPATHLEN*2];
2025 char *p = buf;
2027 VA_START(ap, fmt);
2028 fmt = DoNLS(fmt);
2029 (void)vsnprintf(p, sizeof(buf) - 100, fmt, VA_ARGS(ap));
2030 VA_END(ap);
2031 if (err)
2033 p += strlen(p);
2034 *p++ = ':';
2035 *p++ = ' ';
2036 strncpy(p, strerror(err), buf + sizeof(buf) - p - 1);
2037 buf[sizeof(buf) - 1] = 0;
2039 debug3("Panic('%s'); display=%x displays=%x\n", buf, display, displays);
2040 if (displays == 0 && display == 0)
2042 printf("%s\r\n", buf);
2043 if (PanicPid)
2044 Kill(PanicPid, SIG_BYE);
2046 else if (displays == 0)
2048 /* no displays but a display - must have forked.
2049 * send message to backend!
2051 char *tty = D_usertty;
2052 display = 0;
2053 SendErrorMsg(tty, buf);
2054 sleep(2);
2055 _exit(1);
2057 else
2058 for (display = displays; display; display = display->d_next)
2060 if (D_status)
2061 RemoveStatus();
2062 FinitTerm();
2063 Flush();
2064 #ifdef UTMPOK
2065 RestoreLoginSlot();
2066 #endif
2067 SetTTY(D_userfd, &D_OldMode);
2068 fcntl(D_userfd, F_SETFL, 0);
2069 write(D_userfd, buf, strlen(buf));
2070 write(D_userfd, "\n", 1);
2071 freetty();
2072 if (D_userpid)
2073 Kill(D_userpid, SIG_BYE);
2075 #ifdef MULTIUSER
2076 if (tty_oldmode >= 0)
2078 # ifdef USE_SETEUID
2079 if (setuid(own_uid))
2080 xseteuid(own_uid); /* may be a loop. sigh. */
2081 # else
2082 setuid(own_uid);
2083 # endif
2084 debug1("Panic: changing back modes from %s\n", attach_tty);
2085 chmod(attach_tty, tty_oldmode);
2087 #endif
2088 eexit(1);
2093 * '^' is allowed as an escape mechanism for control characters. jw.
2095 * Added time insertion using ideas/code from /\ndy Jones
2096 * (andy@lingua.cltr.uq.OZ.AU) - thanks a lot!
2100 #ifndef USE_LOCALE
2101 static const char days[] = "SunMonTueWedThuFriSat";
2102 static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
2103 #endif
2105 static char winmsg_buf[MAXSTR];
2106 #define MAX_WINMSG_REND 16 /* rendition changes */
2107 static int winmsg_rend[MAX_WINMSG_REND];
2108 static int winmsg_rendpos[MAX_WINMSG_REND];
2109 static int winmsg_numrend;
2111 static char *
2112 pad_expand(buf, p, numpad, padlen)
2113 char *buf;
2114 char *p;
2115 int numpad;
2116 int padlen;
2118 char *pn, *pn2;
2119 int i, r;
2121 padlen = padlen - (p - buf); /* space for rent */
2122 if (padlen < 0)
2123 padlen = 0;
2124 pn2 = pn = p + padlen;
2125 r = winmsg_numrend;
2126 while (p >= buf)
2128 if (r && p - buf == winmsg_rendpos[r - 1])
2130 winmsg_rendpos[--r] = pn - buf;
2131 continue;
2133 *pn-- = *p;
2134 if (*p-- == 127)
2136 pn[1] = ' ';
2137 i = numpad > 0 ? (padlen + numpad - 1) / numpad : 0;
2138 padlen -= i;
2139 while (i-- > 0)
2140 *pn-- = ' ';
2141 numpad--;
2144 return pn2;
2147 struct backtick {
2148 struct backtick *next;
2149 int num;
2150 int tick;
2151 int lifespan;
2152 time_t bestbefore;
2153 char result[MAXSTR];
2154 char **cmdv;
2155 struct event ev;
2156 char *buf;
2157 int bufi;
2160 struct backtick *backticks;
2162 static void
2163 backtick_filter(bt)
2164 struct backtick *bt;
2166 char *p, *q;
2167 int c;
2169 for (p = q = bt->result; (c = (unsigned char)*p++) != 0;)
2171 if (c == '\t')
2172 c = ' ';
2173 if (c >= ' ' || c == '\005')
2174 *q++ = c;
2176 *q = 0;
2179 static void
2180 backtick_fn(ev, data)
2181 struct event *ev;
2182 char *data;
2184 struct backtick *bt;
2185 int i, j, k, l;
2187 bt = (struct backtick *)data;
2188 debug1("backtick_fn for #%d\n", bt->num);
2189 i = bt->bufi;
2190 l = read(ev->fd, bt->buf + i, MAXSTR - i);
2191 if (l <= 0)
2193 debug1("EOF on backtick #%d\n", bt->num);
2194 evdeq(ev);
2195 close(ev->fd);
2196 ev->fd = -1;
2197 return;
2199 debug1("read %d bytes\n", l);
2200 i += l;
2201 for (j = 0; j < l; j++)
2202 if (bt->buf[i - j - 1] == '\n')
2203 break;
2204 if (j < l)
2206 for (k = i - j - 2; k >= 0; k--)
2207 if (bt->buf[k] == '\n')
2208 break;
2209 k++;
2210 bcopy(bt->buf + k, bt->result, i - j - k);
2211 bt->result[i - j - k - 1] = 0;
2212 backtick_filter(bt);
2213 WindowChanged(0, '`');
2215 if (j == l && i == MAXSTR)
2217 j = MAXSTR/2;
2218 l = j + 1;
2220 if (j < l)
2222 if (j)
2223 bcopy(bt->buf + i - j, bt->buf, j);
2224 i = j;
2226 bt->bufi = i;
2229 void
2230 setbacktick(num, lifespan, tick, cmdv)
2231 int num;
2232 int lifespan;
2233 int tick;
2234 char **cmdv;
2236 struct backtick **btp, *bt;
2237 char **v;
2239 debug1("setbacktick called for backtick #%d\n", num);
2240 for (btp = &backticks; (bt = *btp) != 0; btp = &bt->next)
2241 if (bt->num == num)
2242 break;
2243 if (!bt && !cmdv)
2244 return;
2245 if (bt)
2247 for (v = bt->cmdv; *v; v++)
2248 free(*v);
2249 free(bt->cmdv);
2250 if (bt->buf)
2251 free(bt->buf);
2252 if (bt->ev.fd >= 0)
2253 close(bt->ev.fd);
2254 evdeq(&bt->ev);
2256 if (bt && !cmdv)
2258 *btp = bt->next;
2259 free(bt);
2260 return;
2262 if (!bt)
2264 bt = (struct backtick *)malloc(sizeof *bt);
2265 if (!bt)
2267 Msg(0, strnomem);
2268 return;
2270 bzero(bt, sizeof(*bt));
2271 bt->next = 0;
2272 *btp = bt;
2274 bt->num = num;
2275 bt->tick = tick;
2276 bt->lifespan = lifespan;
2277 bt->bestbefore = 0;
2278 bt->result[0] = 0;
2279 bt->buf = 0;
2280 bt->bufi = 0;
2281 bt->cmdv = cmdv;
2282 bt->ev.fd = -1;
2283 if (bt->tick == 0 && bt->lifespan == 0)
2285 debug("setbacktick: continuous mode\n");
2286 bt->buf = (char *)malloc(MAXSTR);
2287 if (bt->buf == 0)
2289 Msg(0, strnomem);
2290 setbacktick(num, 0, 0, (char **)0);
2291 return;
2293 bt->ev.type = EV_READ;
2294 bt->ev.fd = readpipe(bt->cmdv);
2295 bt->ev.handler = backtick_fn;
2296 bt->ev.data = (char *)bt;
2297 if (bt->ev.fd >= 0)
2298 evenq(&bt->ev);
2302 static char *
2303 runbacktick(bt, tickp, now)
2304 struct backtick *bt;
2305 int *tickp;
2306 time_t now;
2308 int f, i, l, j;
2309 time_t now2;
2311 debug1("runbacktick called for backtick #%d\n", bt->num);
2312 if (bt->tick && (!*tickp || bt->tick < *tickp))
2313 *tickp = bt->tick;
2314 if ((bt->lifespan == 0 && bt->tick == 0) || now < bt->bestbefore)
2316 debug1("returning old result (%d)\n", bt->lifespan);
2317 return bt->result;
2319 f = readpipe(bt->cmdv);
2320 if (f == -1)
2321 return bt->result;
2322 i = 0;
2323 while ((l = read(f, bt->result + i, sizeof(bt->result) - i)) > 0)
2325 debug1("runbacktick: read %d bytes\n", l);
2326 i += l;
2327 for (j = 1; j < l; j++)
2328 if (bt->result[i - j - 1] == '\n')
2329 break;
2330 if (j == l && i == sizeof(bt->result))
2332 j = sizeof(bt->result) / 2;
2333 l = j + 1;
2335 if (j < l)
2337 bcopy(bt->result + i - j, bt->result, j);
2338 i = j;
2341 close(f);
2342 bt->result[sizeof(bt->result) - 1] = '\n';
2343 if (i && bt->result[i - 1] == '\n')
2344 i--;
2345 debug1("runbacktick: finished, %d bytes\n", i);
2346 bt->result[i] = 0;
2347 backtick_filter(bt);
2348 (void)time(&now2);
2349 bt->bestbefore = now2 + bt->lifespan;
2350 return bt->result;
2353 char *
2354 MakeWinMsgEv(str, win, esc, padlen, ev, rec)
2355 char *str;
2356 struct win *win;
2357 int esc;
2358 int padlen;
2359 struct event *ev;
2360 int rec;
2362 static int tick;
2363 char *s = str;
2364 register char *p = winmsg_buf;
2365 register int ctrl;
2366 struct timeval now;
2367 struct tm *tm;
2368 int l, i, r;
2369 int num;
2370 int zeroflg;
2371 int longflg;
2372 int minusflg;
2373 int plusflg;
2374 int qmflag = 0, omflag = 0, qmnumrend = 0;
2375 char *qmpos = 0;
2376 int numpad = 0;
2377 int lastpad = 0;
2378 int truncpos = -1;
2379 int truncper = 0;
2380 int trunclong = 0;
2381 struct backtick *bt;
2383 if (winmsg_numrend >= 0)
2384 winmsg_numrend = 0;
2385 else
2386 winmsg_numrend = -winmsg_numrend;
2388 tick = 0;
2389 tm = 0;
2390 ctrl = 0;
2391 gettimeofday(&now, NULL);
2392 for (; *s && (l = winmsg_buf + MAXSTR - 1 - p) > 0; s++, p++)
2394 *p = *s;
2395 if (ctrl)
2397 ctrl = 0;
2398 if (*s != '^' && *s >= 64)
2399 *p &= 0x1f;
2400 continue;
2402 if (*s != esc)
2404 if (esc == '%')
2406 switch (*s)
2408 #if 0
2409 case '~':
2410 *p = BELL;
2411 break;
2412 #endif
2413 case '^':
2414 ctrl = 1;
2415 *p-- = '^';
2416 break;
2417 default:
2418 break;
2421 continue;
2423 if (*++s == esc) /* double escape ? */
2424 continue;
2425 if ((plusflg = *s == '+') != 0)
2426 s++;
2427 if ((minusflg = *s == '-') != 0)
2428 s++;
2429 if ((zeroflg = *s == '0') != 0)
2430 s++;
2431 num = 0;
2432 while(*s >= '0' && *s <= '9')
2433 num = num * 10 + (*s++ - '0');
2434 if ((longflg = *s == 'L') != 0)
2435 s++;
2436 switch (*s)
2438 case '?':
2439 p--;
2440 if (qmpos)
2442 if ((!qmflag && !omflag) || omflag == 1)
2444 p = qmpos;
2445 if (qmnumrend < winmsg_numrend)
2446 winmsg_numrend = qmnumrend;
2448 qmpos = 0;
2449 break;
2451 qmpos = p;
2452 qmnumrend = winmsg_numrend;
2453 qmflag = omflag = 0;
2454 break;
2455 case ':':
2456 p--;
2457 if (!qmpos)
2458 break;
2459 if (qmflag && omflag != 1)
2461 omflag = 1;
2462 qmpos = p;
2463 qmnumrend = winmsg_numrend;
2465 else
2467 p = qmpos;
2468 if (qmnumrend < winmsg_numrend)
2469 winmsg_numrend = qmnumrend;
2470 omflag = -1;
2472 break;
2473 case 'd': case 'D': case 'm': case 'M': case 'y': case 'Y':
2474 case 'a': case 'A': case 's': case 'c': case 'C':
2475 if (l < 4)
2476 break;
2477 if (tm == 0)
2479 time_t nowsec = now.tv_sec;
2480 tm = localtime(&nowsec);
2482 qmflag = 1;
2483 if (!tick || tick > 3600)
2484 tick = 3600;
2485 switch (*s)
2487 case 'd':
2488 sprintf(p, "%02d", tm->tm_mday % 100);
2489 break;
2490 case 'D':
2491 #ifdef USE_LOCALE
2492 strftime(p, l, (longflg ? "%A" : "%a"), tm);
2493 #else
2494 sprintf(p, "%3.3s", days + 3 * tm->tm_wday);
2495 #endif
2496 break;
2497 case 'm':
2498 sprintf(p, "%02d", tm->tm_mon + 1);
2499 break;
2500 case 'M':
2501 #ifdef USE_LOCALE
2502 strftime(p, l, (longflg ? "%B" : "%b"), tm);
2503 #else
2504 sprintf(p, "%3.3s", months + 3 * tm->tm_mon);
2505 #endif
2506 break;
2507 case 'y':
2508 sprintf(p, "%02d", tm->tm_year % 100);
2509 break;
2510 case 'Y':
2511 sprintf(p, "%04d", tm->tm_year + 1900);
2512 break;
2513 case 'a':
2514 sprintf(p, tm->tm_hour >= 12 ? "pm" : "am");
2515 break;
2516 case 'A':
2517 sprintf(p, tm->tm_hour >= 12 ? "PM" : "AM");
2518 break;
2519 case 's':
2520 sprintf(p, "%02d", tm->tm_sec);
2521 tick = 1;
2522 break;
2523 case 'c':
2524 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", tm->tm_hour, tm->tm_min);
2525 if (!tick || tick > 60)
2526 tick = 60;
2527 break;
2528 case 'C':
2529 sprintf(p, zeroflg ? "%02d:%02d" : "%2d:%02d", (tm->tm_hour + 11) % 12 + 1, tm->tm_min);
2530 if (!tick || tick > 60)
2531 tick = 60;
2532 break;
2533 default:
2534 break;
2536 p += strlen(p) - 1;
2537 break;
2538 case 'l':
2539 #ifdef LOADAV
2540 *p = 0;
2541 if (l > 20)
2542 AddLoadav(p);
2543 if (*p)
2545 qmflag = 1;
2546 p += strlen(p) - 1;
2548 else
2549 *p = '?';
2550 if (!tick || tick > 60)
2551 tick = 60;
2552 #else
2553 *p = '?';
2554 #endif
2555 p += strlen(p) - 1;
2556 break;
2557 case '`':
2558 case 'h':
2559 if (rec >= 10 || (*s == 'h' && (win == 0 || win->w_hstatus == 0 || *win->w_hstatus == 0)))
2561 p--;
2562 break;
2564 if (*s == '`')
2566 for (bt = backticks; bt; bt = bt->next)
2567 if (bt->num == num)
2568 break;
2569 if (bt == 0)
2571 p--;
2572 break;
2576 char savebuf[sizeof(winmsg_buf)];
2577 int oldtick = tick;
2578 int oldnumrend = winmsg_numrend;
2580 *p = 0;
2581 strcpy(savebuf, winmsg_buf);
2582 winmsg_numrend = -winmsg_numrend;
2583 MakeWinMsgEv(*s == 'h' ? win->w_hstatus : runbacktick(bt, &oldtick, now.tv_sec), win, '\005', 0, (struct event *)0, rec + 1);
2584 debug2("oldtick=%d tick=%d\n", oldtick, tick);
2585 if (!tick || oldtick < tick)
2586 tick = oldtick;
2587 if ((int)strlen(winmsg_buf) < l)
2588 strcat(savebuf, winmsg_buf);
2589 strcpy(winmsg_buf, savebuf);
2590 while (oldnumrend < winmsg_numrend)
2591 winmsg_rendpos[oldnumrend++] += p - winmsg_buf;
2592 if (*p)
2593 qmflag = 1;
2594 p += strlen(p) - 1;
2596 break;
2597 case 'w':
2598 case 'W':
2600 struct win *oldfore = 0;
2601 char *ss;
2603 if (display)
2605 oldfore = D_fore;
2606 D_fore = win;
2608 ss = AddWindows(p, l - 1, (*s == 'w' ? 0 : 1) | (longflg ? 0 : 2) | (plusflg ? 4 : 0), win ? win->w_number : -1);
2609 if (minusflg)
2610 *ss = 0;
2611 if (display)
2612 D_fore = oldfore;
2614 if (*p)
2615 qmflag = 1;
2616 p += strlen(p) - 1;
2617 break;
2618 case 'u':
2619 *p = 0;
2620 if (win)
2621 AddOtherUsers(p, l - 1, win);
2622 if (*p)
2623 qmflag = 1;
2624 p += strlen(p) - 1;
2625 break;
2626 case 'f':
2627 *p = 0;
2628 if (win)
2629 AddWindowFlags(p, l - 1, win);
2630 if (*p)
2631 qmflag = 1;
2632 p += strlen(p) - 1;
2633 break;
2634 case 't':
2635 *p = 0;
2636 if (win && (int)strlen(win->w_title) < l)
2638 strcpy(p, win->w_title);
2639 if (*p)
2640 qmflag = 1;
2642 p += strlen(p) - 1;
2643 break;
2644 case '{':
2646 char rbuf[128];
2647 s++;
2648 for (i = 0; i < 127; i++)
2649 if (s[i] && s[i] != '}')
2650 rbuf[i] = s[i];
2651 else
2652 break;
2653 if (s[i] == '}' && winmsg_numrend < MAX_WINMSG_REND)
2655 r = -1;
2656 rbuf[i] = 0;
2657 debug1("MakeWinMsg attrcolor %s\n", rbuf);
2658 if (i != 1 || rbuf[0] != '-')
2659 r = ParseAttrColor(rbuf, (char *)0, 0);
2660 if (r != -1 || (i == 1 && rbuf[0] == '-'))
2662 winmsg_rend[winmsg_numrend] = r;
2663 winmsg_rendpos[winmsg_numrend] = p - winmsg_buf;
2664 winmsg_numrend++;
2667 s += i;
2668 p--;
2670 break;
2671 case 'H':
2672 *p = 0;
2673 if ((int)strlen(HostName) < l)
2675 strcpy(p, HostName);
2676 if (*p)
2677 qmflag = 1;
2679 p += strlen(p) - 1;
2680 break;
2681 case 'S':
2683 char *session_name;
2684 *p = 0;
2685 session_name = strchr(SockName, '.') + 1;
2686 if ((int)strlen(session_name) < l)
2688 strcpy(p, session_name);
2689 if (*p)
2690 qmflag = 1;
2692 p += strlen(p) - 1;
2694 break;
2695 case 'F':
2696 p--;
2697 /* small hack */
2698 if (display && ((ev && ev == &D_forecv->c_captev) || (!ev && win && win == D_fore)))
2699 minusflg = !minusflg;
2700 if (minusflg)
2701 qmflag = 1;
2702 break;
2703 case '>':
2704 truncpos = p - winmsg_buf;
2705 truncper = num > 100 ? 100 : num;
2706 trunclong = longflg;
2707 p--;
2708 break;
2709 case '=':
2710 case '<':
2711 *p = ' ';
2712 if (num || zeroflg || plusflg || longflg || (*s != '='))
2714 /* expand all pads */
2715 if (minusflg)
2717 num = (plusflg ? lastpad : padlen) - num;
2718 if (!plusflg && padlen == 0)
2719 num = p - winmsg_buf;
2720 plusflg = 0;
2722 else if (!zeroflg)
2724 if (*s != '=' && num == 0 && !plusflg)
2725 num = 100;
2726 if (num > 100)
2727 num = 100;
2728 if (padlen == 0)
2729 num = p - winmsg_buf;
2730 else
2731 num = (padlen - (plusflg ? lastpad : 0)) * num / 100;
2733 if (num < 0)
2734 num = 0;
2735 if (plusflg)
2736 num += lastpad;
2737 if (num > MAXSTR - 1)
2738 num = MAXSTR - 1;
2739 if (numpad)
2740 p = pad_expand(winmsg_buf, p, numpad, num);
2741 numpad = 0;
2742 if (p - winmsg_buf > num && !longflg)
2744 int left, trunc;
2746 if (truncpos == -1)
2748 truncpos = lastpad;
2749 truncper = 0;
2751 trunc = lastpad + truncper * (num - lastpad) / 100;
2752 if (trunc > num)
2753 trunc = num;
2754 if (trunc < lastpad)
2755 trunc = lastpad;
2756 left = truncpos - trunc;
2757 if (left > p - winmsg_buf - num)
2758 left = p - winmsg_buf - num;
2759 debug1("lastpad = %d, ", lastpad);
2760 debug3("truncpos = %d, trunc = %d, left = %d\n", truncpos, trunc, left);
2761 if (left > 0)
2763 if (left + lastpad > p - winmsg_buf)
2764 left = p - winmsg_buf - lastpad;
2765 if (p - winmsg_buf - lastpad - left > 0)
2766 bcopy(winmsg_buf + lastpad + left, winmsg_buf + lastpad, p - winmsg_buf - lastpad - left);
2767 p -= left;
2768 r = winmsg_numrend;
2769 while (r && winmsg_rendpos[r - 1] > lastpad)
2771 r--;
2772 winmsg_rendpos[r] -= left;
2773 if (winmsg_rendpos[r] < lastpad)
2774 winmsg_rendpos[r] = lastpad;
2776 if (trunclong)
2778 if (p - winmsg_buf > lastpad)
2779 winmsg_buf[lastpad] = '.';
2780 if (p - winmsg_buf > lastpad + 1)
2781 winmsg_buf[lastpad + 1] = '.';
2782 if (p - winmsg_buf > lastpad + 2)
2783 winmsg_buf[lastpad + 2] = '.';
2786 if (p - winmsg_buf > num)
2788 p = winmsg_buf + num;
2789 if (trunclong)
2791 if (num - 1 >= lastpad)
2792 p[-1] = '.';
2793 if (num - 2 >= lastpad)
2794 p[-2] = '.';
2795 if (num - 3 >= lastpad)
2796 p[-3] = '.';
2798 r = winmsg_numrend;
2799 while (r && winmsg_rendpos[r - 1] > num)
2800 winmsg_rendpos[--r] = num;
2802 truncpos = -1;
2803 trunclong = 0;
2804 if (lastpad > p - winmsg_buf)
2805 lastpad = p - winmsg_buf;
2806 debug1("lastpad now %d\n", lastpad);
2808 if (*s == '=')
2810 while (p - winmsg_buf < num)
2811 *p++ = ' ';
2812 lastpad = p - winmsg_buf;
2813 truncpos = -1;
2814 trunclong = 0;
2815 debug1("lastpad2 now %d\n", lastpad);
2817 p--;
2819 else if (padlen)
2821 *p = 127; /* internal pad representation */
2822 numpad++;
2824 break;
2825 case 'n':
2826 s++;
2827 /* FALLTHROUGH */
2828 default:
2829 s--;
2830 if (l > 10 + num)
2832 if (num == 0)
2833 num = 1;
2834 if (!win)
2835 sprintf(p, "%*s", num, num > 1 ? "--" : "-");
2836 else
2837 sprintf(p, "%*d", num, win->w_number);
2838 qmflag = 1;
2839 p += strlen(p) - 1;
2841 break;
2844 if (qmpos && !qmflag)
2845 p = qmpos + 1;
2846 *p = '\0';
2847 if (numpad)
2849 if (padlen > MAXSTR - 1)
2850 padlen = MAXSTR - 1;
2851 p = pad_expand(winmsg_buf, p, numpad, padlen);
2853 if (ev)
2855 evdeq(ev); /* just in case */
2856 ev->timeout.tv_sec = 0;
2857 ev->timeout.tv_usec = 0;
2859 if (ev && tick)
2861 now.tv_usec = 100000;
2862 if (tick == 1)
2863 now.tv_sec++;
2864 else
2865 now.tv_sec += tick - (now.tv_sec % tick);
2866 ev->timeout = now;
2867 debug2("NEW timeout %d %d\n", ev->timeout.tv_sec, tick);
2869 return winmsg_buf;
2872 char *
2873 MakeWinMsg(s, win, esc)
2874 char *s;
2875 struct win *win;
2876 int esc;
2878 return MakeWinMsgEv(s, win, esc, 0, (struct event *)0, 0);
2881 void
2882 PutWinMsg(s, start, max)
2883 char *s;
2884 int start, max;
2886 int i, p, l, r, n;
2887 struct mchar rend;
2888 struct mchar rendstack[MAX_WINMSG_REND];
2889 int rendstackn = 0;
2891 if (s != winmsg_buf)
2893 /* sorry, no fancy coloring available */
2894 debug1("PutWinMsg %s plain\n", s);
2895 l = strlen(s);
2896 if (l > max)
2897 l = max;
2898 l -= start;
2899 s += start;
2900 while (l-- > 0)
2901 PUTCHARLP(*s++);
2902 return;
2904 rend = D_rend;
2905 p = 0;
2906 l = strlen(s);
2907 debug2("PutWinMsg %s start attr %x\n", s, rend.attr);
2908 for (i = 0; i < winmsg_numrend && max > 0; i++)
2910 if (p > winmsg_rendpos[i] || winmsg_rendpos[i] > l)
2911 break;
2912 if (p < winmsg_rendpos[i])
2914 n = winmsg_rendpos[i] - p;
2915 if (n > max)
2916 n = max;
2917 max -= n;
2918 p += n;
2919 while(n-- > 0)
2921 if (start-- > 0)
2922 s++;
2923 else
2924 PUTCHARLP(*s++);
2927 r = winmsg_rend[i];
2928 if (r == -1)
2930 if (rendstackn > 0)
2931 rend = rendstack[--rendstackn];
2933 else
2935 rendstack[rendstackn++] = rend;
2936 ApplyAttrColor(r, &rend);
2938 SetRendition(&rend);
2940 if (p < l)
2942 n = l - p;
2943 if (n > max)
2944 n = max;
2945 while(n-- > 0)
2947 if (start-- > 0)
2948 s++;
2949 else
2950 PUTCHARLP(*s++);
2956 #ifdef DEBUG
2957 static void
2958 fds1(i, j)
2959 int i, j;
2961 while (i < j)
2963 debug1("%d ", i);
2964 i++;
2966 if ((j = open("/dev/null", 0)) >= 0)
2968 fds1(i + 1, j);
2969 close(j);
2971 else
2973 while (dup(++i) < 0 && errno != EBADF)
2974 debug1("%d ", i);
2975 debug1(" [%d]\n", i);
2979 static void
2980 fds()
2982 debug("fds: ");
2983 fds1(-1, -1);
2985 #endif
2987 static void
2988 serv_read_fn(ev, data)
2989 struct event *ev;
2990 char *data;
2992 debug("Knock - knock!\n");
2993 ReceiveMsg();
2996 static void
2997 serv_select_fn(ev, data)
2998 struct event *ev;
2999 char *data;
3001 struct win *p;
3003 debug("serv_select_fn called\n");
3004 /* XXX: messages?? */
3005 if (GotSigChld)
3007 SigChldHandler();
3009 if (InterruptPlease)
3011 debug("Backend received interrupt\n");
3012 /* This approach is rather questionable in a multi-display
3013 * environment */
3014 if (fore && displays)
3016 #if defined(TERMIO) || defined(POSIX)
3017 char ibuf = displays->d_OldMode.tio.c_cc[VINTR];
3018 #else
3019 char ibuf = displays->d_OldMode.m_tchars.t_intrc;
3020 #endif
3021 #ifdef PSEUDOS
3022 write(W_UWP(fore) ? fore->w_pwin->p_ptyfd : fore->w_ptyfd,
3023 &ibuf, 1);
3024 debug1("Backend wrote interrupt to %d", fore->w_number);
3025 debug1("%s\n", W_UWP(fore) ? " (pseudowin)" : "");
3026 #else
3027 write(fore->w_ptyfd, &ibuf, 1);
3028 debug1("Backend wrote interrupt to %d\n", fore->w_number);
3029 #endif
3031 InterruptPlease = 0;
3034 for (p = windows; p; p = p->w_next)
3036 if (p->w_bell == BELL_FOUND || p->w_bell == BELL_VISUAL)
3038 struct canvas *cv;
3039 int visual = p->w_bell == BELL_VISUAL || visual_bell;
3040 p->w_bell = BELL_ON;
3041 for (display = displays; display; display = display->d_next)
3043 for (cv = D_cvlist; cv; cv = cv->c_next)
3044 if (cv->c_layer->l_bottom == &p->w_layer)
3045 break;
3046 if (cv == 0)
3048 p->w_bell = BELL_DONE;
3049 Msg(0, "%s", MakeWinMsg(BellString, p, '%'));
3051 else if (visual && !D_VB && (!D_status || !D_status_bell))
3053 Msg(0, "%s", VisualBellString);
3054 if (D_status)
3056 D_status_bell = 1;
3057 debug1("using vbell timeout %d\n", VBellWait);
3058 SetTimeout(&D_statusev, VBellWait );
3062 /* don't annoy the user with two messages */
3063 if (p->w_monitor == MON_FOUND)
3064 p->w_monitor = MON_DONE;
3065 WindowChanged(p, 'f');
3067 if (p->w_monitor == MON_FOUND)
3069 struct canvas *cv;
3070 p->w_monitor = MON_ON;
3071 for (display = displays; display; display = display->d_next)
3073 for (cv = D_cvlist; cv; cv = cv->c_next)
3074 if (cv->c_layer->l_bottom == &p->w_layer)
3075 break;
3076 if (cv)
3077 continue; /* user already sees window */
3078 #ifdef MULTIUSER
3079 if (!(ACLBYTE(p->w_mon_notify, D_user->u_id) & ACLBIT(D_user->u_id)))
3080 continue; /* user doesn't care */
3081 #endif
3082 Msg(0, "%s", MakeWinMsg(ActivityString, p, '%'));
3083 p->w_monitor = MON_DONE;
3085 WindowChanged(p, 'f');
3089 for (display = displays; display; display = display->d_next)
3091 struct canvas *cv;
3092 if (D_status == STATUS_ON_WIN)
3093 continue;
3094 /* XXX: should use display functions! */
3095 for (cv = D_cvlist; cv; cv = cv->c_next)
3097 int lx, ly;
3099 /* normalize window, see resize.c */
3100 lx = cv->c_layer->l_x;
3101 ly = cv->c_layer->l_y;
3102 if (lx == cv->c_layer->l_width)
3103 lx--;
3104 if (ly + cv->c_yoff < cv->c_ys)
3106 int i, n = cv->c_ys - (ly + cv->c_yoff);
3107 cv->c_yoff = cv->c_ys - ly;
3108 RethinkViewportOffsets(cv);
3109 if (n > cv->c_layer->l_height)
3110 n = cv->c_layer->l_height;
3111 CV_CALL(cv,
3112 LScrollV(flayer, -n, 0, flayer->l_height - 1, 0);
3113 LayRedisplayLine(-1, -1, -1, 1);
3114 for (i = 0; i < n; i++)
3115 LayRedisplayLine(i, 0, flayer->l_width - 1, 1);
3116 if (cv == cv->c_display->d_forecv)
3117 LaySetCursor();
3120 else if (ly + cv->c_yoff > cv->c_ye)
3122 int i, n = ly + cv->c_yoff - cv->c_ye;
3123 cv->c_yoff = cv->c_ye - ly;
3124 RethinkViewportOffsets(cv);
3125 if (n > cv->c_layer->l_height)
3126 n = cv->c_layer->l_height;
3127 CV_CALL(cv,
3128 LScrollV(flayer, n, 0, cv->c_layer->l_height - 1, 0);
3129 LayRedisplayLine(-1, -1, -1, 1);
3130 for (i = 0; i < n; i++)
3131 LayRedisplayLine(i + flayer->l_height - n, 0, flayer->l_width - 1, 1);
3132 if (cv == cv->c_display->d_forecv)
3133 LaySetCursor();
3136 if (lx + cv->c_xoff < cv->c_xs)
3138 int i, n = cv->c_xs - (lx + cv->c_xoff);
3139 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3140 n = (cv->c_xe - cv->c_xs + 1) / 2;
3141 if (cv->c_xoff + n > cv->c_xs)
3142 n = cv->c_xs - cv->c_xoff;
3143 cv->c_xoff += n;
3144 RethinkViewportOffsets(cv);
3145 if (n > cv->c_layer->l_width)
3146 n = cv->c_layer->l_width;
3147 CV_CALL(cv,
3148 LayRedisplayLine(-1, -1, -1, 1);
3149 for (i = 0; i < flayer->l_height; i++)
3151 LScrollH(flayer, -n, i, 0, flayer->l_width - 1, 0, 0);
3152 LayRedisplayLine(i, 0, n - 1, 1);
3154 if (cv == cv->c_display->d_forecv)
3155 LaySetCursor();
3158 else if (lx + cv->c_xoff > cv->c_xe)
3160 int i, n = lx + cv->c_xoff - cv->c_xe;
3161 if (n < (cv->c_xe - cv->c_xs + 1) / 2)
3162 n = (cv->c_xe - cv->c_xs + 1) / 2;
3163 if (cv->c_xoff - n + cv->c_layer->l_width - 1 < cv->c_xe)
3164 n = cv->c_xoff + cv->c_layer->l_width - 1 - cv->c_xe;
3165 cv->c_xoff -= n;
3166 RethinkViewportOffsets(cv);
3167 if (n > cv->c_layer->l_width)
3168 n = cv->c_layer->l_width;
3169 CV_CALL(cv,
3170 LayRedisplayLine(-1, -1, -1, 1);
3171 for (i = 0; i < flayer->l_height; i++)
3173 LScrollH(flayer, n, i, 0, flayer->l_width - 1, 0, 0);
3174 LayRedisplayLine(i, flayer->l_width - n, flayer->l_width - 1, 1);
3176 if (cv == cv->c_display->d_forecv)
3177 LaySetCursor();
3183 for (display = displays; display; display = display->d_next)
3185 if (D_status == STATUS_ON_WIN || D_cvlist == 0 || D_cvlist->c_next == 0)
3186 continue;
3187 debug1("serv_select_fn: Restore on cv %#x\n", (int)D_forecv);
3188 CV_CALL(D_forecv, LayRestore();LaySetCursor());
3192 static void
3193 logflush_fn(ev, data)
3194 struct event *ev;
3195 char *data;
3197 struct win *p;
3198 char *buf;
3199 int n;
3201 if (!islogfile(NULL))
3202 return; /* no more logfiles */
3203 logfflush(NULL);
3204 n = log_flush ? log_flush : (logtstamp_after + 4) / 5;
3205 if (n)
3207 SetTimeout(ev, n * 1000);
3208 evenq(ev); /* re-enqueue ourself */
3210 if (!logtstamp_on)
3211 return;
3212 /* write fancy time-stamp */
3213 for (p = windows; p; p = p->w_next)
3215 if (!p->w_log)
3216 continue;
3217 p->w_logsilence += n;
3218 if (p->w_logsilence < logtstamp_after)
3219 continue;
3220 if (p->w_logsilence - n >= logtstamp_after)
3221 continue;
3222 buf = MakeWinMsg(logtstamp_string, p, '%');
3223 logfwrite(p->w_log, buf, strlen(buf));
3228 * Interprets ^?, ^@ and other ^-control-char notation.
3229 * Interprets \ddd octal notation
3231 * The result is placed in *cp, p is advanced behind the parsed expression and
3232 * returned.
3234 static char *
3235 ParseChar(p, cp)
3236 char *p, *cp;
3238 if (*p == 0)
3239 return 0;
3240 if (*p == '^' && p[1])
3242 if (*++p == '?')
3243 *cp = '\177';
3244 else if (*p >= '@')
3245 *cp = Ctrl(*p);
3246 else
3247 return 0;
3248 ++p;
3250 else if (*p == '\\' && *++p <= '7' && *p >= '0')
3252 *cp = 0;
3254 *cp = *cp * 8 + *p - '0';
3255 while (*++p <= '7' && *p >= '0');
3257 else
3258 *cp = *p++;
3259 return p;
3262 static int
3263 ParseEscape(p)
3264 char *p;
3266 unsigned char buf[2];
3268 if (*p == 0)
3269 SetEscape((struct acluser *)0, -1, -1);
3270 else
3272 if ((p = ParseChar(p, (char *)buf)) == NULL ||
3273 (p = ParseChar(p, (char *)buf+1)) == NULL || *p)
3274 return -1;
3275 SetEscape((struct acluser *)0, buf[0], buf[1]);
3277 return 0;