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