new yacc has different yyfix command
[unix-history] / usr / src / old / sysline / sysline.c
CommitLineData
e1e8d3ca
KM
1/*
2 * Copyright (c) 1980 Regents of the University of California.
f9e4427b
KB
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
9c50374f
KB
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
e1e8d3ca
KM
16 */
17
18#ifndef lint
19char copyright[] =
20"@(#) Copyright (c) 1980 Regents of the University of California.\n\
21 All rights reserved.\n";
f9e4427b 22#endif /* not lint */
e1e8d3ca
KM
23
24#ifndef lint
bcc006b0 25static char sccsid[] = "@(#)sysline.c 5.14 (Berkeley) %G%";
f9e4427b 26#endif /* not lint */
e1e8d3ca
KM
27
28/*
29 * sysline - system status display on 25th line of terminal
30 * j.k.foderaro
31 *
32 * Prints a variety of information on the special status line of terminals
33 * that have a status display capability. Cursor motions, status commands,
34 * etc. are gleamed from /etc/termcap.
35 * By default, all information is printed, and flags are given on the command
36 * line to disable the printing of information. The information and
37 * disabling flags are:
38 *
39 * flag what
40 * ----- ----
41 * time of day
42 * load average and change in load average in the last 5 mins
43 * number of user logged on
44 * -p # of processes the users owns which are runnable and the
45 * number which are suspended. Processes whose parent is 1
46 * are not counted.
47 * -l users who've logged on and off.
48 * -m summarize new mail which has arrived
49 *
50 * <other flags>
51 * -r use non reverse video
52 * -c turn off 25th line for 5 seconds before redisplaying.
53 * -b beep once one the half hour, twice on the hour
54 * +N refresh display every N seconds.
55 * -i print pid first thing
56 * -e do simple print designed for an emacs buffer line
57 * -w do the right things for a window
58 * -h print hostname between time and load average
59 * -D print day/date before time of day
60 * -d debug mode - print status line data in human readable format
61 * -q quiet mode - don't output diagnostic messages
62 * -s print Short (left-justified) line if escapes not allowed
63 * -j Print left Justified line regardless
64 */
65
66#define BSD4_2 /* for 4.2 BSD */
67#define WHO /* turn this on always */
68#define HOSTNAME /* 4.1a or greater, with hostname() */
69#define RWHO /* 4.1a or greater, with rwho */
70#define VMUNIX /* turn this on if you are running on vmunix */
71#define NEW_BOOTTIME /* 4.1c or greater */
72
73#define NETPREFIX "ucb"
74#define DEFDELAY 60 /* update status once per minute */
e1e8d3ca
KM
75/*
76 * if MAXLOAD is defined, then if the load average exceeded MAXLOAD
77 * then the process table will not be scanned and the log in/out data
78 * will not be checked. The purpose of this is to reduced the load
79 * on the system when it is loaded.
80 */
81#define MAXLOAD 6.0
82
83#include <stdio.h>
84#include <sys/param.h>
bcc006b0 85#include <sys/types.h>
e1e8d3ca
KM
86#include <sys/signal.h>
87#include <utmp.h>
88#include <ctype.h>
89#ifndef BSD4_2
90#include <unctrl.h>
91#endif
92#include <sys/time.h>
93#include <sys/stat.h>
94#ifdef VMUNIX
95#include <nlist.h>
96#include <sys/vtimes.h>
97#include <sys/proc.h>
98#endif
99#ifdef pdp11
100#include <a.out.h>
101#include <sys/proc.h>
102#endif
103#include <curses.h>
104#undef nl
105#ifdef TERMINFO
106#include <term.h>
107#endif TERMINFO
108
109#ifdef RWHO
6efb822a
MK
110#include <protocols/rwhod.h>
111
e1e8d3ca 112#define DOWN_THRESHOLD (11 * 60)
e1e8d3ca 113
e1e8d3ca
KM
114struct remotehost {
115 char *rh_host;
116 int rh_file;
117} remotehost[10];
118int nremotes = 0;
119#endif RWHO
120
b528bd5e
KB
121#include "pathnames.h"
122
e1e8d3ca
KM
123struct nlist nl[] = {
124#ifdef NEW_BOOTTIME
125 { "_boottime" }, /* After 4.1a the label changed to "boottime" */
126#else
127 { "_bootime" }, /* Under 4.1a and earlier it is "bootime" */
128#endif
129#define NL_BOOT 0
130 { "_proc" },
131#define NL_PROC 1
e1e8d3ca
KM
132#ifdef VMUNIX
133 { "_nproc" },
bcc006b0 134#define NL_NPROC 2
e1e8d3ca
KM
135#endif
136 0
137};
138
139 /* stuff for the kernel */
b528bd5e 140int kmem; /* file descriptor for _PATH_KMEM */
e1e8d3ca
KM
141struct proc *proc, *procNPROC;
142int nproc;
143int procadr;
144double avenrun[3]; /* used for storing load averages */
145
146/*
147 * In order to determine how many people are logged on and who has
148 * logged in or out, we read in the /etc/utmp file. We also keep track of
149 * the previous utmp file.
150 */
4874204a 151int ut = -1; /* the file descriptor */
e1e8d3ca
KM
152struct utmp *new, *old;
153char *status; /* per tty status bits, see below */
154int nentries; /* number of utmp entries */
155 /* string lengths for printing */
156#define LINESIZE (sizeof old->ut_line)
157#define NAMESIZE (sizeof old->ut_name)
158/*
159 * Status codes to say what has happened to a particular entry in utmp.
160 * NOCH means no change, ON means new person logged on,
161 * OFF means person logged off.
162 */
163#define NOCH 0
164#define ON 0x1
165#define OFF 0x2
166
167#ifdef WHO
168char whofilename[100];
169char whofilename2[100];
170#endif
171
172#ifdef HOSTNAME
6efb822a 173char hostname[MAXHOSTNAMELEN+1]; /* one more for null termination */
e1e8d3ca
KM
174#endif
175
176char lockfilename[100]; /* if exists, will prevent us from running */
177
178 /* flags which determine which info is printed */
179int mailcheck = 1; /* m - do biff like checking of mail */
180int proccheck = 1; /* p - give information on processes */
181int logcheck = 1; /* l - tell who logs in and out */
182int hostprint = 0; /* h - print out hostname */
183int dateprint = 0; /* h - print out day/date */
184int quiet = 0; /* q - hush diagnostic messages */
185
186 /* flags which determine how things are printed */
187int clr_bet_ref = 0; /* c - clear line between refeshes */
188int reverse = 1; /* r - use reverse video */
189int shortline = 0; /* s - short (left-justified) if escapes not allowed */
190int leftline = 0; /* j - left-justified even if escapes allowed */
191
192 /* flags which have terminal do random things */
193int beep = 0; /* b - beep every half hour and twice every hour */
194int printid = 0; /* i - print pid of this process at startup */
195int synch = 1; /* synchronize with clock */
196
197 /* select output device (status display or straight output) */
198int emacs = 0; /* e - assume status display */
199int window = 0; /* w - window mode */
200int dbug = 0; /* d - debug */
201
202/*
203 * used to turn off reverse video every REVOFF times
204 * in an attempt to not wear out the phospher.
205 */
206#define REVOFF 5
207int revtime = 1;
208
209 /* used by mail checker */
210off_t mailsize = 0;
211off_t linebeg = 0; /* place where we last left off reading */
212
213 /* things used by the string routines */
214int chars; /* number of printable characters */
215char *sp;
216char strarr[512]; /* big enough now? */
217 /* flags to stringdump() */
218char sawmail; /* remember mail was seen to print bells */
219char mustclear; /* status line messed up */
220
221 /* strings which control status line display */
222#ifdef TERMINFO
223char *rev_out, *rev_end, *arrows;
224char *tparm();
225#else
226char to_status_line[64];
227char from_status_line[64];
228char dis_status_line[64];
229char clr_eol[64];
230char rev_out[20], rev_end[20];
231char *arrows, *bell = "\007";
232int eslok; /* escapes on status line okay (reverse, cursor addressing) */
f9e4427b 233int hasws = 0; /* is "ws" explicitly defined? */
e1e8d3ca
KM
234int columns;
235#define tparm(cap, parm) tgoto((cap), 0, (parm))
236char *tgoto();
237#endif
238
239 /* to deal with window size changes */
240#ifdef SIGWINCH
241int sigwinch();
242char winchanged; /* window size has changed since last update */
243#endif
244
245 /* random globals */
246char *username;
247char *ourtty; /* keep track of what tty we're on */
248struct stat stbuf, mstbuf; /* mstbuf for mail check only */
249unsigned delay = DEFDELAY;
658d7227 250uid_t uid;
e1e8d3ca
KM
251double loadavg = 0.0; /* current load average */
252int users = 0;
253
254char *getenv();
255char *ttyname();
256char *strcpy1();
257char *sysrup();
258char *calloc();
259char *malloc();
260int outc();
261int erroutc();
262
263main(argc,argv)
264 register char **argv;
265{
266 int clearbotl();
267 register char *cp;
268 char *home;
897f1e78 269 extern char *index();
e1e8d3ca 270
e1e8d3ca
KM
271#ifdef HOSTNAME
272 gethostname(hostname, sizeof hostname - 1);
897f1e78
JB
273 if ((cp = index(hostname, '.')) != NULL)
274 *cp = '\0';
e1e8d3ca
KM
275#endif
276
277 for (argv++; *argv != 0; argv++)
278 switch (**argv) {
279 case '-':
280 for (cp = *argv + 1; *cp; cp++) {
281 switch(*cp) {
282 case 'r' : /* turn off reverse video */
283 reverse = 0;
284 break;
285 case 'c':
286 clr_bet_ref = 1;
287 break;
288 case 'h':
289 hostprint = 1;
290 break;
291 case 'D':
292 dateprint = 1;
293 break;
294#ifdef RWHO
295 case 'H':
296 if (argv[1] == 0)
297 break;
298 argv++;
299 if (strcmp(hostname, *argv) &&
300 strcmp(&hostname[sizeof NETPREFIX - 1], *argv))
301 remotehost[nremotes++].rh_host = *argv;
302 break;
303#endif RWHO
304 case 'm':
305 mailcheck = 0;
306 break;
307 case 'p':
308 proccheck = 0;
309 break;
310 case 'l':
311 logcheck = 0;
312 break;
313 case 'b':
314 beep = 1;
315 break;
316 case 'i':
317 printid = 1;
318 break;
319 case 'w':
320 window = 1;
321 break;
322 case 'e':
323 emacs = 1;
324 break;
325 case 'd':
326 dbug = 1;
327 break;
328 case 'q':
329 quiet = 1;
330 break;
331 case 's':
332 shortline = 1;
333 break;
334 case 'j':
335 leftline = 1;
336 break;
337 default:
338 fprintf(stderr,
339 "sysline: bad flag: %c\n", *cp);
340 }
341 }
342 break;
343 case '+':
344 delay = atoi(*argv + 1);
345 if (delay < 10)
346 delay = 10;
347 else if (delay > 500)
348 delay = 500;
349 synch = 0; /* no more sync */
350 break;
351 default:
352 fprintf(stderr, "sysline: illegal argument %s\n",
353 argv[0]);
354 }
355 if (emacs) {
356 reverse = 0;
357 columns = 79;
358 } else /* if not to emacs window, initialize terminal dependent info */
359 initterm();
360#ifdef SIGWINCH
361 /*
362 * When the window size changes and we are the foreground
363 * process (true if -w), we get this signal.
364 */
365 signal(SIGWINCH, sigwinch);
366#endif
367 getwinsize(); /* get window size from ioctl */
368
369 /* immediately fork and let the parent die if not emacs mode */
370 if (!emacs && !window && !dbug) {
371 if (fork())
372 exit(0);
373 /* pgrp should take care of things, but ignore them anyway */
374 signal(SIGINT, SIG_IGN);
375 signal(SIGQUIT, SIG_IGN);
376#ifdef VMUNIX
377 signal(SIGTTOU, SIG_IGN);
378#endif
379 }
380 /*
381 * When we logoff, init will do a "vhangup()" on this
382 * tty which turns off I/O access and sends a SIGHUP
383 * signal. We catch this and thereby clear the status
384 * display. Note that a bug in 4.1bsd caused the SIGHUP
385 * signal to be sent to the wrong process, so you had to
386 * `kill -HUP' yourself in your .logout file.
387 * Do the same thing for SIGTERM, which is the default kill
388 * signal.
389 */
390 signal(SIGHUP, clearbotl);
391 signal(SIGTERM, clearbotl);
392 /*
393 * This is so kill -ALRM to force update won't screw us up..
394 */
395 signal(SIGALRM, SIG_IGN);
396
397 uid = getuid();
398 ourtty = ttyname(2); /* remember what tty we are on */
399 if (printid) {
400 printf("%d\n", getpid());
401 fflush(stdout);
402 }
403 dup2(2, 1);
404
405 if ((home = getenv("HOME")) == 0)
406 home = "";
407 strcpy1(strcpy1(whofilename, home), "/.who");
408 strcpy1(strcpy1(whofilename2, home), "/.sysline");
409 strcpy1(strcpy1(lockfilename, home), "/.syslinelock");
410
b528bd5e
KB
411 if ((kmem = open(_PATH_KMEM,0)) < 0) {
412 fprintf(stderr, "Can't open %s\n", _PATH_KMEM);
e1e8d3ca
KM
413 exit(1);
414 }
415 readnamelist();
416 if (proccheck)
417 initprocread();
e1e8d3ca
KM
418 if (mailcheck)
419 if ((username = getenv("USER")) == 0)
420 mailcheck = 0;
421 else {
b528bd5e 422 chdir(_PATH_MBOX);
e1e8d3ca
KM
423 if (stat(username, &mstbuf) >= 0)
424 mailsize = mstbuf.st_size;
425 else
426 mailsize = 0;
427 }
428
429 while (emacs || window || isloggedin())
430 if (access(lockfilename, 0) >= 0)
431 sleep(60);
432 else {
433 prtinfo();
434 sleep(delay);
435 if (clr_bet_ref) {
436 tputs(dis_status_line, 1, outc);
437 fflush(stdout);
438 sleep(5);
439 }
440 revtime = (1 + revtime) % REVOFF;
441 }
442 clearbotl();
443 /*NOTREACHED*/
444}
445
446isloggedin()
447{
448 /*
449 * you can tell if a person has logged out if the owner of
450 * the tty has changed
451 */
452 struct stat statbuf;
453
454 return fstat(2, &statbuf) == 0 && statbuf.st_uid == uid;
455}
456
457readnamelist()
458{
459 time_t bootime, clock, nintv, time();
460
b528bd5e 461 nlist(_PATH_UNIX, nl);
e1e8d3ca
KM
462 if (nl[0].n_value == 0) {
463 if (!quiet)
464 fprintf(stderr, "No namelist\n");
465 return;
466 }
467 lseek(kmem, (long)nl[NL_BOOT].n_value, 0);
468 read(kmem, &bootime, sizeof(bootime));
469 (void) time(&clock);
470 nintv = clock - bootime;
471 if (nintv <= 0L || nintv > 60L*60L*24L*365L) {
472 if (!quiet)
473 fprintf(stderr,
474 "Time makes no sense... namelist must be wrong\n");
bcc006b0 475 nl[NL_PROC].n_value = 0;
e1e8d3ca
KM
476 }
477}
478
4874204a
EW
479readutmp(nflag)
480 char nflag;
e1e8d3ca 481{
4874204a
EW
482 static time_t lastmod; /* initially zero */
483 static off_t utmpsize; /* ditto */
e1e8d3ca
KM
484 struct stat st;
485
b528bd5e
KB
486 if (ut < 0 && (ut = open(_PATH_UTMP, 0)) < 0) {
487 fprintf(stderr, "sysline: Can't open %s.\n", _PATH_UTMP);
e1e8d3ca
KM
488 exit(1);
489 }
4874204a 490 if (fstat(ut, &st) < 0 || st.st_mtime == lastmod)
e1e8d3ca
KM
491 return 0;
492 lastmod = st.st_mtime;
4874204a
EW
493 if (utmpsize != st.st_size) {
494 utmpsize = st.st_size;
495 nentries = utmpsize / sizeof (struct utmp);
496 if (old == 0) {
497 old = (struct utmp *)calloc(utmpsize, 1);
498 new = (struct utmp *)calloc(utmpsize, 1);
499 } else {
500 old = (struct utmp *)realloc((char *)old, utmpsize);
501 new = (struct utmp *)realloc((char *)new, utmpsize);
502 free(status);
503 }
504 status = malloc(nentries * sizeof *status);
505 if (old == 0 || new == 0 || status == 0) {
506 fprintf(stderr, "sysline: Out of memory.\n");
507 exit(1);
508 }
509 }
e1e8d3ca 510 lseek(ut, 0L, 0);
4874204a 511 (void) read(ut, (char *) (nflag ? new : old), utmpsize);
e1e8d3ca
KM
512 return 1;
513}
514
515/*
516 * read in the process table locations and sizes, and allocate space
517 * for storing the process table. This is done only once.
518 */
519initprocread()
520{
521
522 if (nl[NL_PROC].n_value == 0)
523 return;
524#ifdef VMUNIX
525 lseek(kmem, (long)nl[NL_PROC].n_value, 0);
526 read(kmem, &procadr, sizeof procadr);
527 lseek(kmem, (long)nl[NL_NPROC].n_value, 0);
528 read(kmem, &nproc, sizeof nproc);
529#endif
530#ifdef pdp11
531 procadr = nl[NL_PROC].n_value;
532 nproc = NPROC; /* from param.h */
533#endif
534 if ((proc = (struct proc *) calloc(nproc, sizeof (struct proc))) == 0) {
535 fprintf(stderr, "Out of memory.\n");
536 exit(1);
537 }
538 procNPROC = proc + nproc;
539}
540
541/*
542 * read in the process table. This assumes that initprocread has alread been
543 * called to set up storage.
544 */
545readproctab()
546{
547
548 if (nl[NL_PROC].n_value == 0)
549 return (0);
550 lseek(kmem, (long)procadr, 0);
551 read(kmem, (char *)proc, nproc * sizeof (struct proc));
552 return (1);
553}
554
555prtinfo()
556{
557 int on, off;
558 register i;
559 char fullprocess;
560
561 stringinit();
562#ifdef SIGWINCH
563 if (winchanged) {
564 winchanged = 0;
565 getwinsize();
566 mustclear = 1;
567 }
568#endif
569#ifdef WHO
570 /* check for file named .who in the home directory */
571 whocheck();
572#endif
573 timeprint();
574 /*
575 * if mail is seen, don't print rest of info, just the mail
576 * reverse new and old so that next time we run, we won't lose log
577 * in and out information
578 */
579 if (mailcheck && (sawmail = mailseen()))
580 goto bottom;
581#ifdef HOSTNAME
582#ifdef RWHO
583 for (i = 0; i < nremotes; i++) {
584 char *tmp;
585
586 stringspace();
587 tmp = sysrup(remotehost + i);
588 stringcat(tmp, strlen(tmp));
589 }
590#endif
591 /*
592 * print hostname info if requested
593 */
594 if (hostprint) {
595 stringspace();
596 stringcat(hostname, -1);
597 }
598#endif
599 /*
600 * print load average and difference between current load average
601 * and the load average 5 minutes ago
602 */
bcc006b0 603 if (getloadavg(avenrun, 3) > 0) {
e1e8d3ca
KM
604 double diff;
605
606 stringspace();
bcc006b0 607
e1e8d3ca
KM
608 if ((diff = avenrun[0] - avenrun[1]) < 0.0)
609 stringprt("%.1f %.1f", avenrun[0], diff);
610 else
611 stringprt("%.1f +%.1f", avenrun[0], diff);
612 loadavg = avenrun[0]; /* remember load average */
613 }
614 /*
615 * print log on and off information
616 */
617 stringspace();
618 fullprocess = 1;
619#ifdef MAXLOAD
620 if (loadavg > MAXLOAD)
621 fullprocess = 0; /* too loaded to run */
622#endif
623 /*
624 * Read utmp file (logged in data) only if we are doing a full
625 * process, or if this is the first time and we are calculating
626 * the number of users.
627 */
628 on = off = 0;
629 if (users == 0) { /* first time */
4874204a 630 if (readutmp(0))
e1e8d3ca
KM
631 for (i = 0; i < nentries; i++)
632 if (old[i].ut_name[0])
633 users++;
4874204a 634 } else if (fullprocess && readutmp(1)) {
e1e8d3ca
KM
635 struct utmp *tmp;
636
637 users = 0;
638 for (i = 0; i < nentries; i++) {
639 if (strncmp(old[i].ut_name,
640 new[i].ut_name, NAMESIZE) == 0)
641 status[i] = NOCH;
642 else if (old[i].ut_name[0] == '\0') {
643 status[i] = ON;
644 on++;
645 } else if (new[i].ut_name[0] == '\0') {
646 status[i] = OFF;
647 off++;
648 } else {
649 status[i] = ON | OFF;
650 on++;
651 off++;
652 }
653 if (new[i].ut_name[0])
654 users++;
655 }
656 tmp = new;
657 new = old;
658 old = tmp;
659 }
660 /*
661 * Print:
662 * 1. number of users
663 * 2. a * for unread mail
664 * 3. a - if load is too high
665 * 4. number of processes running and stopped
666 */
667 stringprt("%du", users);
668 if (mailsize > 0 && mstbuf.st_mtime >= mstbuf.st_atime)
669 stringcat("*", -1);
670 if (!fullprocess && (proccheck || logcheck))
671 stringcat("-", -1);
672 if (fullprocess && proccheck && readproctab()) {
673 register struct proc *p;
674 int procrun, procstop;
675
676 /*
677 * We are only interested in processes which have the same
678 * uid as us, and whose parent process id is not 1.
679 */
680 procrun = procstop = 0;
681 for (p = proc; p < procNPROC; p++) {
682 if (p->p_stat == 0 || p->p_pgrp == 0 ||
683 p->p_uid != uid || p->p_ppid == 1)
684 continue;
685 switch (p->p_stat) {
686 case SSTOP:
687 procstop++;
688 break;
689 case SSLEEP:
690 /*
691 * Sleep can mean waiting for a signal or just
692 * in a disk or page wait queue ready to run.
693 * We can tell if it is the later by the pri
694 * being negative.
695 */
696 if (p->p_pri < PZERO)
697 procrun++;
698 break;
699 case SWAIT:
700 case SRUN:
701 case SIDL:
702 procrun++;
703 }
704 }
705 if (procrun > 0 || procstop > 0) {
706 stringspace();
707 if (procrun > 0 && procstop > 0)
708 stringprt("%dr %ds", procrun, procstop);
709 else if (procrun > 0)
710 stringprt("%dr", procrun);
711 else
712 stringprt("%ds", procstop);
713 }
714 }
715 /*
716 * If anyone has logged on or off, and we are interested in it,
717 * print it out.
718 */
719 if (logcheck) {
720 /* old and new have already been swapped */
721 if (on) {
722 stringspace();
723 stringcat("on:", -1);
724 for (i = 0; i < nentries; i++)
725 if (status[i] & ON) {
726 stringprt(" %.8s", old[i].ut_name);
727 ttyprint(old[i].ut_line);
728 }
729 }
730 if (off) {
731 stringspace();
732 stringcat("off:", -1);
733 for (i = 0; i < nentries; i++)
734 if (status[i] & OFF) {
735 stringprt(" %.8s", new[i].ut_name);
736 ttyprint(new[i].ut_line);
737 }
738 }
739 }
740bottom:
741 /* dump out what we know */
742 stringdump();
743}
744
745timeprint()
746{
747 long curtime;
748 struct tm *tp, *localtime();
749 static int beepable = 1;
750
751 /* always print time */
752 time(&curtime);
753 tp = localtime(&curtime);
754 if (dateprint)
755 stringprt("%.11s", ctime(&curtime));
756 stringprt("%d:%02d", tp->tm_hour > 12 ? tp->tm_hour - 12 :
757 (tp->tm_hour == 0 ? 12 : tp->tm_hour), tp->tm_min);
758 if (synch) /* sync with clock */
759 delay = 60 - tp->tm_sec;
760 /*
761 * Beepable is used to insure that we get at most one set of beeps
762 * every half hour.
763 */
764 if (beep)
765 if (beepable) {
766 if (tp->tm_min == 30) {
767 tputs(bell, 1, outc);
768 fflush(stdout);
769 beepable = 0;
770 } else if (tp->tm_min == 0) {
771 tputs(bell, 1, outc);
772 fflush(stdout);
773 sleep(2);
774 tputs(bell, 1, outc);
775 fflush(stdout);
776 beepable = 0;
777 }
778 } else
779 if (tp->tm_min != 0 && tp->tm_min != 30)
780 beepable = 1;
781}
782
783/*
784 * whocheck -- check for file named .who and print it on the who line first
785 */
786whocheck()
787{
788 int chss;
789 register char *p;
790 char buff[81];
791 int whofile;
792
793 if ((whofile = open(whofilename, 0)) < 0 &&
794 (whofile = open(whofilename2, 0)) < 0)
795 return;
796 chss = read(whofile, buff, sizeof buff - 1);
797 close(whofile);
798 if (chss <= 0)
799 return;
800 buff[chss] = '\0';
801 /*
802 * Remove all line feeds, and replace by spaces if they are within
803 * the message, else replace them by nulls.
804 */
805 for (p = buff; *p;)
806 if (*p == '\n')
807 if (p[1])
808 *p++ = ' ';
809 else
810 *p = '\0';
811 else
812 p++;
813 stringcat(buff, p - buff);
814 stringspace();
815}
816
817/*
818 * ttyprint -- given the name of a tty, print in the string buffer its
819 * short name surrounded by parenthesis.
820 * ttyxx is printed as (xx)
821 * console is printed as (cty)
822 */
823ttyprint(name)
824 char *name;
825{
826 char buff[11];
827
828 if (strncmp(name, "tty", 3) == 0)
829 stringprt("(%.*s)", LINESIZE - 3, name + 3);
830 else if (strcmp(name, "console") == 0)
831 stringcat("(cty)", -1);
832 else
833 stringprt("(%.*s)", LINESIZE, name);
834}
835
836/*
837 * mail checking function
838 * returns 0 if no mail seen
839 */
840mailseen()
841{
842 FILE *mfd;
843 register n;
844 register char *cp;
845 char lbuf[100], sendbuf[100], *bufend;
846 char seenspace;
847 int retval = 0;
848
849 if (stat(username, &mstbuf) < 0) {
850 mailsize = 0;
851 return 0;
852 }
853 if (mstbuf.st_size <= mailsize || (mfd = fopen(username,"r")) == NULL) {
854 mailsize = mstbuf.st_size;
855 return 0;
856 }
857 fseek(mfd, mailsize, 0);
858 while ((n = readline(mfd, lbuf, sizeof lbuf)) >= 0 &&
859 strncmp(lbuf, "From ", 5) != 0)
860 ;
861 if (n < 0) {
f9e4427b 862 stringcat("Mail has just arrived", -1);
e1e8d3ca
KM
863 goto out;
864 }
865 retval = 1;
866 /*
867 * Found a From line, get second word, which is the sender,
868 * and print it.
869 */
870 for (cp = lbuf + 5; *cp && *cp != ' '; cp++) /* skip to blank */
871 ;
872 *cp = '\0'; /* terminate name */
873 stringspace();
874 stringprt("Mail from %s ", lbuf + 5);
875 /*
876 * Print subject, and skip over header.
877 */
878 while ((n = readline(mfd, lbuf, sizeof lbuf)) > 0)
879 if (strncmp(lbuf, "Subject:", 8) == 0)
880 stringprt("on %s ", lbuf + 9);
881 if (!emacs)
882 stringcat(arrows, 2);
883 else
884 stringcat(": ", 2);
885 if (n < 0) /* already at eof */
886 goto out;
887 /*
888 * Print as much of the letter as we can.
889 */
890 cp = sendbuf;
891 if ((n = columns - chars) > sizeof sendbuf - 1)
892 n = sizeof sendbuf - 1;
893 bufend = cp + n;
894 seenspace = 0;
895 while ((n = readline(mfd, lbuf, sizeof lbuf)) >= 0) {
896 register char *rp;
897
898 if (strncmp(lbuf, "From ", 5) == 0)
899 break;
900 if (cp >= bufend)
901 continue;
902 if (!seenspace) {
903 *cp++ = ' '; /* space before lines */
904 seenspace = 1;
905 }
906 rp = lbuf;
907 while (*rp && cp < bufend)
908 if (isspace(*rp)) {
909 if (!seenspace) {
910 *cp++ = ' ';
911 seenspace = 1;
912 }
913 rp++;
914 } else {
915 *cp++ = *rp++;
916 seenspace = 0;
917 }
918 }
919 *cp = 0;
920 stringcat(sendbuf, -1);
921 /*
922 * Want to update write time so a star will
923 * appear after the number of users until the
924 * user reads his mail.
925 */
926out:
927 mailsize = linebeg;
928 fclose(mfd);
929 touch(username);
930 return retval;
931}
932
933/*
934 * readline -- read a line from fp and store it in buf.
935 * return the number of characters read.
936 */
937readline(fp, buf, n)
938 register FILE *fp;
939 char *buf;
940 register n;
941{
942 register c;
943 register char *cp = buf;
944
945 linebeg = ftell(fp); /* remember loc where line begins */
946 cp = buf;
947 while (--n > 0 && (c = getc(fp)) != EOF && c != '\n')
948 *cp++ = c;
949 *cp = 0;
950 if (c == EOF && cp - buf == 0)
951 return -1;
952 return cp - buf;
953}
954
955
956/*
957 * string hacking functions
958 */
959
960stringinit()
961{
962 sp = strarr;
963 chars = 0;
964}
965
966/*VARARGS1*/
967stringprt(format, a, b, c)
968 char *format;
969{
970 char tempbuf[150];
971
06eef7c4
KB
972 (void)sprintf(tempbuf, format, a, b, c);
973 stringcat(tempbuf, -1);
e1e8d3ca
KM
974}
975
976stringdump()
977{
978 char bigbuf[sizeof strarr + 200];
979 register char *bp = bigbuf;
980 register int i;
981
982 if (!emacs) {
983 if (sawmail)
984 bp = strcpy1(bp, bell);
985 if (eslok)
986 bp = strcpy1(bp, tparm(to_status_line,
987 leftline ? 0 : columns - chars));
988 else {
989 bp = strcpy1(bp, to_status_line);
990 if (!shortline && !leftline)
991 for (i = columns - chars; --i >= 0;)
992 *bp++ = ' ';
993 }
994 if (reverse && revtime != 0)
995 bp = strcpy1(bp, rev_out);
996 }
997 *sp = 0;
998 bp = strcpy1(bp, strarr);
999 if (!emacs) {
1000 if (reverse)
1001 bp = strcpy1(bp, rev_end);
1002 bp = strcpy1(bp, from_status_line);
1003 if (sawmail)
1004 bp = strcpy1(strcpy1(bp, bell), bell);
1005 *bp = 0;
1006 tputs(bigbuf, 1, outc);
1007 if (mustclear) {
1008 mustclear = 0;
1009 tputs(clr_eol, 1, outc);
1010 }
1011 if (dbug)
1012 putchar('\n');
1013 fflush(stdout);
1014 } else
1015 write(2, bigbuf, bp - bigbuf);
1016}
1017
1018stringspace()
1019{
1020 if (reverse && revtime != 0) {
1021#ifdef TERMINFO
1022 stringcat(rev_end,
1023 magic_cookie_glitch <= 0 ? 0 : magic_cookie_glitch);
1024 stringcat(" ", 1);
1025 stringcat(rev_out,
1026 magic_cookie_glitch <= 0 ? 0 : magic_cookie_glitch);
1027#else
1028 stringcat(rev_end, 0);
1029 stringcat(" ", 1);
1030 stringcat(rev_out, 0);
1031#endif TERMINFO
1032 } else
1033 stringcat(" ", 1);
1034}
1035
1036/*
1037 * stringcat :: concatenate the characters in string str to the list we are
1038 * building to send out.
1039 * str - the string to print. may contain funny (terminal control) chars.
1040 * n - the number of printable characters in the string
1041 * or if -1 then str is all printable so we can truncate it,
1042 * otherwise don't print only half a string.
1043 */
1044stringcat(str, n)
1045 register char *str;
1046 register n;
1047{
1048 register char *p = sp;
1049
1050 if (n < 0) { /* truncate */
1051 n = columns - chars;
1052 while ((*p++ = *str++) && --n >= 0)
1053 ;
1054 p--;
1055 chars += p - sp;
1056 sp = p;
1057 } else if (chars + n <= columns) { /* don't truncate */
1058 while (*p++ = *str++)
1059 ;
1060 chars += n;
1061 sp = p - 1;
1062 }
1063}
1064
1065/*
1066 * touch :: update the modify time of a file.
1067 */
1068touch(name)
1069 char *name; /* name of file */
1070{
1071 register fd;
1072 char buf;
1073
1074 if ((fd = open(name, 2)) >= 0) {
1075 read(fd, &buf, 1); /* get first byte */
1076 lseek(fd, 0L, 0); /* go to beginning */
1077 write(fd, &buf, 1); /* and rewrite first byte */
1078 close(fd);
1079 }
1080}
1081
1082
1083/*
1084 * clearbotl :: clear bottom line.
1085 * called when process quits or is killed.
1086 * it clears the bottom line of the terminal.
1087 */
1088clearbotl()
1089{
1090 register int fd;
1091 int exit();
1092
1093 signal(SIGALRM, exit);
1094 alarm(30); /* if can't open in 30 secs, just die */
1095 if (!emacs && (fd = open(ourtty, 1)) >= 0) {
1096 write(fd, dis_status_line, strlen(dis_status_line));
1097 close(fd);
1098 }
1099#ifdef PROF
b528bd5e
KB
1100 if (chdir(_PATH_SYSLINE) < 0)
1101 (void) chdir(_PATH_TMP);
e1e8d3ca
KM
1102#endif
1103 exit(0);
1104}
1105
1106#ifdef TERMINFO
1107initterm()
1108{
1109 static char standbuf[40];
1110
1111 setupterm(0, 1, 0);
1112 if (!window && !has_status_line) {
1113 /* not an appropriate terminal */
1114 if (!quiet)
1115 fprintf(stderr, "sysline: no status capability for %s\n",
1116 getenv("TERM"));
1117 exit(1);
1118 }
1119 if (window || status_line_esc_ok) {
1120 if (set_attributes) {
1121 /* reverse video mode */
1122 strcpy(standbuf,
1123 tparm(set_attributes,0,0,1,0,0,0,0,0,0));
1124 rev_out = standbuf;
1125 rev_end = exit_attribute_mode;
1126 } else if (enter_standout_mode && exit_standout_mode) {
1127 rev_out = enter_standout_mode;
1128 rev_end = exit_standout_mode;
1129 } else
1130 rev_out = rev_end = "";
1131 } else
1132 rev_out = rev_end = "";
1133 columns--; /* avoid cursor wraparound */
1134}
1135
1136#else /* TERMCAP */
1137
1138initterm()
1139{
1140 char *term, *cp;
1141 static char tbuf[1024];
1142 char is2[40];
1143 extern char *UP;
1144
1145 if ((term = getenv("TERM")) == NULL) {
1146 if (!quiet)
1147 fprintf(stderr,
1148 "sysline: No TERM variable in enviroment\n");
1149 exit(1);
1150 }
1151 if (tgetent(tbuf, term) <= 0) {
1152 if (!quiet)
1153 fprintf(stderr,
1154 "sysline: Unknown terminal type: %s\n", term);
1155 exit(1);
1156 }
1157 if (!window && tgetflag("hs") <= 0) {
1158 if (!strncmp(term, "h19", 3)) {
1159 /* for upward compatability with h19sys */
1160 strcpy(to_status_line,
1161 "\033j\033x5\033x1\033Y8%+ \033o");
1162 strcpy(from_status_line, "\033k\033y5");
1163 strcpy(dis_status_line, "\033y1");
1164 strcpy(rev_out, "\033p");
1165 strcpy(rev_end, "\033q");
1166 arrows = "\033Fhh\033G";
1167 columns = 80;
1168 UP = "\b";
1169 return;
1170 }
1171 if (!quiet)
1172 fprintf(stderr,
1173 "sysline: No status capability for %s\n", term);
1174 exit(1);
1175 }
1176 cp = is2;
1177 if (tgetstr("i2", &cp) != NULL) {
1178 /* someday tset will do this */
1179 tputs(is2, 1, erroutc);
1180 fflush(stdout);
1181 }
1182
1183 /* the "-1" below is to avoid cursor wraparound problems */
42f1384d 1184 columns = tgetnum("ws");
f9e4427b
KB
1185 hasws = columns >= 0;
1186 if (!hasws)
42f1384d
KB
1187 columns = tgetnum("co");
1188 columns -= 1;
e1e8d3ca
KM
1189 if (window) {
1190 strcpy(to_status_line, "\r");
e1e8d3ca
KM
1191 cp = dis_status_line; /* use the clear line sequence */
1192 *cp++ = '\r';
1193 tgetstr("ce", &cp);
e2e4d5a4
MK
1194 if (leftline)
1195 strcpy(from_status_line, dis_status_line + 1);
1196 else
1197 strcpy(from_status_line, "");
e1e8d3ca
KM
1198 } else {
1199 cp = to_status_line;
1200 tgetstr("ts", &cp);
1201 cp = from_status_line;
1202 tgetstr("fs", &cp);
1203 cp = dis_status_line;
1204 tgetstr("ds", &cp);
1205 eslok = tgetflag("es");
1206 }
1207 if (eslok || window) {
1208 cp = rev_out;
1209 tgetstr("so", &cp);
1210 cp = rev_end;
1211 tgetstr("se", &cp);
1212 cp = clr_eol;
1213 tgetstr("ce", &cp);
1214 } else
1215 reverse = 0; /* turn off reverse video */
1216 UP = "\b";
1217 if (!strncmp(term, "h19", 3))
1218 arrows = "\033Fhh\033G"; /* "two tiny graphic arrows" */
1219 else
1220 arrows = "->";
1221}
1222#endif TERMINFO
1223
e1e8d3ca
KM
1224#ifdef RWHO
1225char *
1226sysrup(hp)
1227 register struct remotehost *hp;
1228{
1229 char filename[100];
1230 struct whod wd;
45ac4a67 1231#define WHOD_HDR_SIZE (sizeof (wd) - sizeof (wd.wd_we))
e1e8d3ca
KM
1232 static char buffer[50];
1233 time_t now;
1234
1235 /*
1236 * rh_file is initially 0.
1237 * This is ok since standard input is assumed to exist.
1238 */
1239 if (hp->rh_file == 0) {
1240 /*
1241 * Try rwho hostname file, and if that fails try ucbhostname.
1242 */
b528bd5e 1243 (void) strcpy1(strcpy1(filename, _PATH_RWHO), hp->rh_host);
e1e8d3ca 1244 if ((hp->rh_file = open(filename, 0)) < 0) {
b528bd5e 1245 (void) strcpy1(strcpy1(strcpy1(filename, _PATH_RWHO),
e1e8d3ca
KM
1246 NETPREFIX), hp->rh_host);
1247 hp->rh_file = open(filename, 0);
1248 }
1249 }
42f1384d
KB
1250 if (hp->rh_file < 0) {
1251 (void) sprintf(buffer, "%s?", hp->rh_host);
1252 return(buffer);
1253 }
e1e8d3ca 1254 (void) lseek(hp->rh_file, (off_t)0, 0);
42f1384d
KB
1255 if (read(hp->rh_file, (char *)&wd, WHOD_HDR_SIZE) != WHOD_HDR_SIZE) {
1256 (void) sprintf(buffer, "%s ?", hp->rh_host);
1257 return(buffer);
1258 }
e1e8d3ca
KM
1259 (void) time(&now);
1260 if (now - wd.wd_recvtime > DOWN_THRESHOLD) {
1261 long interval;
1262 long days, hours, minutes;
1263
1264 interval = now - wd.wd_recvtime;
1265 minutes = (interval + 59) / 60; /* round to minutes */
1266 hours = minutes / 60; /* extract hours from minutes */
1267 minutes %= 60; /* remove hours from minutes */
1268 days = hours / 24; /* extract days from hours */
1269 hours %= 24; /* remove days from hours */
1270 if (days > 7 || days < 0)
1271 (void) sprintf(buffer, "%s down", hp->rh_host);
1272 else if (days > 0)
1273 (void) sprintf(buffer, "%s %d+%d:%02d",
1274 hp->rh_host, days, hours, minutes);
1275 else
1276 (void) sprintf(buffer, "%s %d:%02d",
1277 hp->rh_host, hours, minutes);
1278 } else
1279 (void) sprintf(buffer, "%s %.1f",
1280 hp->rh_host, wd.wd_loadav[0]/100.0);
1281 return buffer;
1282}
1283#endif RWHO
1284
1285getwinsize()
1286{
1287#ifdef TIOCGWINSZ
1288 struct winsize winsize;
1289
1290 /* the "-1" below is to avoid cursor wraparound problems */
f9e4427b
KB
1291 if (!hasws && ioctl(2, TIOCGWINSZ, (char *)&winsize) >= 0 &&
1292 winsize.ws_col != 0)
e1e8d3ca
KM
1293 columns = winsize.ws_col - 1;
1294#endif
1295}
1296
1297#ifdef SIGWINCH
1298sigwinch()
1299{
1300 winchanged++;
1301}
1302#endif
1303
1304char *
1305strcpy1(p, q)
1306 register char *p, *q;
1307{
1308
1309 while (*p++ = *q++)
1310 ;
1311 return p - 1;
1312}
1313
1314outc(c)
1315 char c;
1316{
1317 if (dbug)
1318 printf("%s", unctrl(c));
1319 else
1320 putchar(c);
1321}
1322
1323erroutc(c)
1324 char c;
1325{
1326 if (dbug)
1327 fprintf(stderr, "%s", unctrl(c));
1328 else
1329 putc(c, stderr);
1330}