tc.who.c revision 69408
169408Sache/* $Header: /src/pub/tcsh/tc.who.c,v 3.32 2000/11/12 02:18:07 christos Exp $ */
259243Sobrien/*
359243Sobrien * tc.who.c: Watch logins and logouts...
459243Sobrien */
559243Sobrien/*-
659243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
759243Sobrien * All rights reserved.
859243Sobrien *
959243Sobrien * Redistribution and use in source and binary forms, with or without
1059243Sobrien * modification, are permitted provided that the following conditions
1159243Sobrien * are met:
1259243Sobrien * 1. Redistributions of source code must retain the above copyright
1359243Sobrien *    notice, this list of conditions and the following disclaimer.
1459243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1559243Sobrien *    notice, this list of conditions and the following disclaimer in the
1659243Sobrien *    documentation and/or other materials provided with the distribution.
1759243Sobrien * 3. All advertising materials mentioning features or use of this software
1859243Sobrien *    must display the following acknowledgement:
1959243Sobrien *	This product includes software developed by the University of
2059243Sobrien *	California, Berkeley and its contributors.
2159243Sobrien * 4. Neither the name of the University nor the names of its contributors
2259243Sobrien *    may be used to endorse or promote products derived from this software
2359243Sobrien *    without specific prior written permission.
2459243Sobrien *
2559243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2659243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2759243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2859243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2959243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3059243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3159243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3259243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3359243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3459243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3559243Sobrien * SUCH DAMAGE.
3659243Sobrien */
3759243Sobrien#include "sh.h"
3859243Sobrien
3969408SacheRCSID("$Id: tc.who.c,v 3.32 2000/11/12 02:18:07 christos Exp $")
4059243Sobrien
4159243Sobrien#include "tc.h"
4259243Sobrien
4359243Sobrien#ifndef HAVENOUTMP
4459243Sobrien/*
4559243Sobrien * kfk 26 Jan 1984 - for login watch functions.
4659243Sobrien */
4759243Sobrien#include <ctype.h>
4859243Sobrien
4959243Sobrien#ifdef HAVEUTMPX
5059243Sobrien# include <utmpx.h>
5159243Sobrien/* I just redefine a few words here.  Changing every occurrence below
5259243Sobrien * seems like too much of work.  All UTMP functions have equivalent
5359243Sobrien * UTMPX counterparts, so they can be added all here when needed.
5459243Sobrien * Kimmo Suominen, Oct 14 1991
5559243Sobrien */
5659243Sobrien# ifndef _PATH_UTMP
5769408Sache#  if defined(__UTMPX_FILE) && !defined(UTMPX_FILE)
5869408Sache#   define _PATH_UTMP __UTMPX_FILE
5969408Sache#  else
6069408Sache#   define _PATH_UTMP UTMPX_FILE
6169408Sache#  endif /* __UTMPX_FILE && !UTMPX_FILE */
6259243Sobrien# endif /* _PATH_UTMP */
6359243Sobrien# define utmp utmpx
6469408Sache# ifdef __MVS__
6569408Sache#  define ut_time ut_tv.tv_sec
6669408Sache#  define ut_name ut_user
6769408Sache# else
6869408Sache#  define ut_time ut_xtime
6969408Sache# endif /* __MVS__ */
7059243Sobrien#else /* !HAVEUTMPX */
7169408Sache# ifndef WINNT_NATIVE
7259243Sobrien#  include <utmp.h>
7369408Sache# endif /* WINNT_NATIVE */
7459243Sobrien#endif /* HAVEUTMPX */
7559243Sobrien
7659243Sobrien#ifndef BROKEN_CC
7759243Sobrien# define UTNAMLEN	sizeof(((struct utmp *) 0)->ut_name)
7859243Sobrien# define UTLINLEN	sizeof(((struct utmp *) 0)->ut_line)
7959243Sobrien# ifdef UTHOST
8059243Sobrien#  ifdef _SEQUENT_
8159243Sobrien#   define UTHOSTLEN	100
8259243Sobrien#  else
8359243Sobrien#   define UTHOSTLEN	sizeof(((struct utmp *) 0)->ut_host)
8459243Sobrien#  endif
8559243Sobrien# endif	/* UTHOST */
8659243Sobrien#else
8759243Sobrien/* give poor cc a little help if it needs it */
8859243Sobrienstruct utmp __ut;
8959243Sobrien
9059243Sobrien# define UTNAMLEN	sizeof(__ut.ut_name)
9159243Sobrien# define UTLINLEN	sizeof(__ut.ut_line)
9259243Sobrien# ifdef UTHOST
9359243Sobrien#  ifdef _SEQUENT_
9459243Sobrien#   define UTHOSTLEN	100
9559243Sobrien#  else
9659243Sobrien#   define UTHOSTLEN	sizeof(__ut.ut_host)
9759243Sobrien#  endif
9859243Sobrien# endif /* UTHOST */
9959243Sobrien#endif /* BROKEN_CC */
10059243Sobrien
10159243Sobrien#ifndef _PATH_UTMP
10259243Sobrien# ifdef	UTMP_FILE
10359243Sobrien#  define _PATH_UTMP UTMP_FILE
10459243Sobrien# else
10559243Sobrien#  define _PATH_UTMP "/etc/utmp"
10659243Sobrien# endif /* UTMP_FILE */
10759243Sobrien#endif /* _PATH_UTMP */
10859243Sobrien
10959243Sobrien
11059243Sobrienstruct who {
11159243Sobrien    struct who *who_next;
11259243Sobrien    struct who *who_prev;
11359243Sobrien    char    who_name[UTNAMLEN + 1];
11459243Sobrien    char    who_new[UTNAMLEN + 1];
11559243Sobrien    char    who_tty[UTLINLEN + 1];
11659243Sobrien#ifdef UTHOST
11759243Sobrien    char    who_host[UTHOSTLEN + 1];
11859243Sobrien#endif /* UTHOST */
11959243Sobrien    time_t  who_time;
12059243Sobrien    int     who_status;
12159243Sobrien};
12259243Sobrien
12359243Sobrienstatic struct who whohead, whotail;
12459243Sobrienstatic time_t watch_period = 0;
12559243Sobrienstatic time_t stlast = 0;
12659243Sobrien#ifdef WHODEBUG
12759243Sobrienstatic	void	debugwholist	__P((struct who *, struct who *));
12859243Sobrien#endif
12959243Sobrienstatic	void	print_who	__P((struct who *));
13059243Sobrien
13159243Sobrien
13259243Sobrien#define ONLINE		01
13359243Sobrien#define OFFLINE		02
13459243Sobrien#define CHANGED		04
13559243Sobrien#define STMASK		07
13659243Sobrien#define ANNOUNCE	010
13759243Sobrien
13859243Sobrien/*
13959243Sobrien * Karl Kleinpaste, 26 Jan 1984.
14059243Sobrien * Initialize the dummy tty list for login watch.
14159243Sobrien * This dummy list eliminates boundary conditions
14259243Sobrien * when doing pointer-chase searches.
14359243Sobrien */
14459243Sobrienvoid
14559243Sobrieninitwatch()
14659243Sobrien{
14759243Sobrien    whohead.who_next = &whotail;
14859243Sobrien    whotail.who_prev = &whohead;
14959243Sobrien    stlast = 1;
15059243Sobrien#ifdef WHODEBUG
15159243Sobrien    debugwholist(NULL, NULL);
15259243Sobrien#endif /* WHODEBUG */
15359243Sobrien}
15459243Sobrien
15559243Sobrienvoid
15659243Sobrienresetwatch()
15759243Sobrien{
15859243Sobrien    watch_period = 0;
15959243Sobrien    stlast = 0;
16059243Sobrien}
16159243Sobrien
16259243Sobrien/*
16359243Sobrien * Karl Kleinpaste, 26 Jan 1984.
16459243Sobrien * Watch /etc/utmp for login/logout changes.
16559243Sobrien */
16659243Sobrienvoid
16759243Sobrienwatch_login(force)
16859243Sobrien    int force;
16959243Sobrien{
17059243Sobrien    int     utmpfd, comp = -1, alldone;
17159243Sobrien    int	    firsttime = stlast == 1;
17259243Sobrien#ifdef BSDSIGS
17359243Sobrien    sigmask_t omask;
17459243Sobrien#endif				/* BSDSIGS */
17559243Sobrien    struct utmp utmp;
17659243Sobrien    struct who *wp, *wpnew;
17759243Sobrien    struct varent *v;
17859243Sobrien    Char  **vp = NULL;
17959243Sobrien    time_t  t, interval = MAILINTVL;
18059243Sobrien    struct stat sta;
18159243Sobrien#if defined(UTHOST) && defined(_SEQUENT_)
18259243Sobrien    char   *host, *ut_find_host();
18359243Sobrien#endif
18469408Sache#ifdef WINNT_NATIVE
18559243Sobrien    static int ncbs_posted = 0;
18659243Sobrien    USE(utmp);
18759243Sobrien    USE(utmpfd);
18859243Sobrien    USE(sta);
18959243Sobrien    USE(wpnew);
19069408Sache#endif /* WINNT_NATIVE */
19159243Sobrien
19259243Sobrien    /* stop SIGINT, lest our login list get trashed. */
19359243Sobrien#ifdef BSDSIGS
19459243Sobrien    omask = sigblock(sigmask(SIGINT));
19559243Sobrien#else
19659243Sobrien    (void) sighold(SIGINT);
19759243Sobrien#endif
19859243Sobrien
19959243Sobrien    v = adrof(STRwatch);
20059243Sobrien    if (v == NULL && !force) {
20159243Sobrien#ifdef BSDSIGS
20259243Sobrien	(void) sigsetmask(omask);
20359243Sobrien#else
20459243Sobrien	(void) sigrelse(SIGINT);
20559243Sobrien#endif
20659243Sobrien	return;			/* no names to watch */
20759243Sobrien    }
20859243Sobrien    if (!force) {
20959243Sobrien	trim(vp = v->vec);
21059243Sobrien	if (blklen(vp) % 2)		/* odd # args: 1st == # minutes. */
21159243Sobrien	    interval = (number(*vp)) ? (getn(*vp++) * 60) : MAILINTVL;
21259243Sobrien    }
21359243Sobrien    else
21459243Sobrien	interval = 0;
21559243Sobrien
21659243Sobrien    (void) time(&t);
21769408Sache#ifdef WINNT_NATIVE
21859243Sobrien	/*
21959243Sobrien	 * Since NCB_ASTATs take time, start em async at least 90 secs
22059243Sobrien	 * before we are due -amol 6/5/97
22159243Sobrien	 */
22259243Sobrien	if (!ncbs_posted) {
22359243Sobrien	    unsigned long tdiff = t - watch_period;
22459243Sobrien	    if (!watch_period || ((tdiff  > 0) && (tdiff > (interval - 90)))) {
22559243Sobrien		start_ncbs(vp);
22659243Sobrien 		ncbs_posted = 1;
22759243Sobrien	    }
22859243Sobrien	}
22969408Sache#endif /* WINNT_NATIVE */
23059243Sobrien    if (t - watch_period < interval) {
23159243Sobrien#ifdef BSDSIGS
23259243Sobrien	(void) sigsetmask(omask);
23359243Sobrien#else
23459243Sobrien	(void) sigrelse(SIGINT);
23559243Sobrien#endif
23659243Sobrien	return;			/* not long enough yet... */
23759243Sobrien    }
23859243Sobrien    watch_period = t;
23969408Sache#ifdef WINNT_NATIVE
24059243Sobrien    ncbs_posted = 0;
24169408Sache#else /* !WINNT_NATIVE */
24259243Sobrien
24359243Sobrien    /*
24459243Sobrien     * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de>
24559243Sobrien     * Don't open utmp all the time, stat it first...
24659243Sobrien     */
24759243Sobrien    if (stat(_PATH_UTMP, &sta)) {
24861515Sobrien	if (!force)
24961515Sobrien	    xprintf(CGETS(26, 1,
25061515Sobrien			  "cannot stat %s.  Please \"unset watch\".\n"),
25161515Sobrien		    _PATH_UTMP);
25259243Sobrien# ifdef BSDSIGS
25359243Sobrien	(void) sigsetmask(omask);
25459243Sobrien# else
25559243Sobrien	(void) sigrelse(SIGINT);
25659243Sobrien# endif
25759243Sobrien	return;
25859243Sobrien    }
25959243Sobrien    if (stlast == sta.st_mtime) {
26059243Sobrien# ifdef BSDSIGS
26159243Sobrien	(void) sigsetmask(omask);
26259243Sobrien# else
26359243Sobrien	(void) sigrelse(SIGINT);
26459243Sobrien# endif
26559243Sobrien	return;
26659243Sobrien    }
26759243Sobrien    stlast = sta.st_mtime;
26859243Sobrien    if ((utmpfd = open(_PATH_UTMP, O_RDONLY)) < 0) {
26961515Sobrien	if (!force)
27061515Sobrien	    xprintf(CGETS(26, 2,
27161515Sobrien			  "%s cannot be opened.  Please \"unset watch\".\n"),
27261515Sobrien		    _PATH_UTMP);
27359243Sobrien# ifdef BSDSIGS
27459243Sobrien	(void) sigsetmask(omask);
27559243Sobrien# else
27659243Sobrien	(void) sigrelse(SIGINT);
27759243Sobrien# endif
27859243Sobrien	return;
27959243Sobrien    }
28059243Sobrien
28159243Sobrien    /*
28259243Sobrien     * xterm clears the entire utmp entry - mark everyone on the status list
28359243Sobrien     * OFFLINE or we won't notice X "logouts"
28459243Sobrien     */
28559243Sobrien    for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
28659243Sobrien	wp->who_status = OFFLINE;
28759243Sobrien	wp->who_time = 0;
28859243Sobrien    }
28959243Sobrien
29059243Sobrien    /*
29159243Sobrien     * Read in the utmp file, sort the entries, and update existing entries or
29259243Sobrien     * add new entries to the status list.
29359243Sobrien     */
29459243Sobrien    while (read(utmpfd, (char *) &utmp, sizeof utmp) == sizeof utmp) {
29559243Sobrien
29659243Sobrien# ifdef DEAD_PROCESS
29759243Sobrien#  ifndef IRIS4D
29859243Sobrien	if (utmp.ut_type != USER_PROCESS)
29959243Sobrien	    continue;
30059243Sobrien#  else
30159243Sobrien	/* Why is that? Cause the utmp file is always corrupted??? */
30259243Sobrien	if (utmp.ut_type != USER_PROCESS && utmp.ut_type != DEAD_PROCESS)
30359243Sobrien	    continue;
30459243Sobrien#  endif /* IRIS4D */
30559243Sobrien# endif /* DEAD_PROCESS */
30659243Sobrien
30759243Sobrien	if (utmp.ut_name[0] == '\0' && utmp.ut_line[0] == '\0')
30859243Sobrien	    continue;	/* completely void entry */
30959243Sobrien# ifdef DEAD_PROCESS
31059243Sobrien	if (utmp.ut_type == DEAD_PROCESS && utmp.ut_line[0] == '\0')
31159243Sobrien	    continue;
31259243Sobrien# endif /* DEAD_PROCESS */
31359243Sobrien	wp = whohead.who_next;
31459243Sobrien	while (wp->who_next && (comp = strncmp(wp->who_tty, utmp.ut_line, UTLINLEN)) < 0)
31559243Sobrien	    wp = wp->who_next;/* find that tty! */
31659243Sobrien
31759243Sobrien	if (wp->who_next && comp == 0) {	/* found the tty... */
31859243Sobrien# ifdef DEAD_PROCESS
31959243Sobrien	    if (utmp.ut_type == DEAD_PROCESS) {
32059243Sobrien		wp->who_time = utmp.ut_time;
32159243Sobrien		wp->who_status = OFFLINE;
32259243Sobrien	    }
32359243Sobrien	    else
32459243Sobrien# endif /* DEAD_PROCESS */
32559243Sobrien	    if (utmp.ut_name[0] == '\0') {
32659243Sobrien		wp->who_time = utmp.ut_time;
32759243Sobrien		wp->who_status = OFFLINE;
32859243Sobrien	    }
32959243Sobrien	    else if (strncmp(utmp.ut_name, wp->who_name, UTNAMLEN) == 0) {
33059243Sobrien		/* someone is logged in */
33159243Sobrien		wp->who_time = utmp.ut_time;
33259243Sobrien		wp->who_status = 0;	/* same guy */
33359243Sobrien	    }
33459243Sobrien	    else {
33559243Sobrien		(void) strncpy(wp->who_new, utmp.ut_name, UTNAMLEN);
33659243Sobrien# ifdef UTHOST
33759243Sobrien#  ifdef _SEQUENT_
33859243Sobrien		host = ut_find_host(wp->who_tty);
33959243Sobrien		if (host)
34059243Sobrien		    (void) strncpy(wp->who_host, host, UTHOSTLEN);
34159243Sobrien		else
34259243Sobrien		    wp->who_host[0] = 0;
34359243Sobrien#  else
34459243Sobrien		(void) strncpy(wp->who_host, utmp.ut_host, UTHOSTLEN);
34559243Sobrien#  endif
34659243Sobrien# endif /* UTHOST */
34759243Sobrien		wp->who_time = utmp.ut_time;
34859243Sobrien		if (wp->who_name[0] == '\0')
34959243Sobrien		    wp->who_status = ONLINE;
35059243Sobrien		else
35159243Sobrien		    wp->who_status = CHANGED;
35259243Sobrien	    }
35359243Sobrien	}
35459243Sobrien	else {		/* new tty in utmp */
35559243Sobrien	    wpnew = (struct who *) xcalloc(1, sizeof *wpnew);
35659243Sobrien	    (void) strncpy(wpnew->who_tty, utmp.ut_line, UTLINLEN);
35759243Sobrien# ifdef UTHOST
35859243Sobrien#  ifdef _SEQUENT_
35959243Sobrien	    host = ut_find_host(wpnew->who_tty);
36059243Sobrien	    if (host)
36159243Sobrien		(void) strncpy(wpnew->who_host, host, UTHOSTLEN);
36259243Sobrien	    else
36359243Sobrien		wpnew->who_host[0] = 0;
36459243Sobrien#  else
36559243Sobrien	    (void) strncpy(wpnew->who_host, utmp.ut_host, UTHOSTLEN);
36659243Sobrien#  endif
36759243Sobrien# endif /* UTHOST */
36859243Sobrien	    wpnew->who_time = utmp.ut_time;
36959243Sobrien# ifdef DEAD_PROCESS
37059243Sobrien	    if (utmp.ut_type == DEAD_PROCESS)
37159243Sobrien		wpnew->who_status = OFFLINE;
37259243Sobrien	    else
37359243Sobrien# endif /* DEAD_PROCESS */
37459243Sobrien	    if (utmp.ut_name[0] == '\0')
37559243Sobrien		wpnew->who_status = OFFLINE;
37659243Sobrien	    else {
37759243Sobrien		(void) strncpy(wpnew->who_new, utmp.ut_name, UTNAMLEN);
37859243Sobrien		wpnew->who_status = ONLINE;
37959243Sobrien	    }
38059243Sobrien# ifdef WHODEBUG
38159243Sobrien	    debugwholist(wpnew, wp);
38259243Sobrien# endif /* WHODEBUG */
38359243Sobrien
38459243Sobrien	    wpnew->who_next = wp;	/* link in a new 'who' */
38559243Sobrien	    wpnew->who_prev = wp->who_prev;
38659243Sobrien	    wpnew->who_prev->who_next = wpnew;
38759243Sobrien	    wp->who_prev = wpnew;	/* linked in now */
38859243Sobrien	}
38959243Sobrien    }
39059243Sobrien    (void) close(utmpfd);
39159243Sobrien# if defined(UTHOST) && defined(_SEQUENT_)
39259243Sobrien    endutent();
39359243Sobrien# endif
39469408Sache#endif /* !WINNT_NATIVE */
39559243Sobrien
39659243Sobrien    if (force || vp == NULL)
39759243Sobrien	return;
39859243Sobrien
39959243Sobrien    /*
40059243Sobrien     * The state of all logins is now known, so we can search the user's list
40159243Sobrien     * of watchables to print the interesting ones.
40259243Sobrien     */
40359243Sobrien    for (alldone = 0; !alldone && *vp != NULL && **vp != '\0' &&
40459243Sobrien	 *(vp + 1) != NULL && **(vp + 1) != '\0';
40559243Sobrien	 vp += 2) {		/* args used in pairs... */
40659243Sobrien
40759243Sobrien	if (eq(*vp, STRany) && eq(*(vp + 1), STRany))
40859243Sobrien	    alldone = 1;
40959243Sobrien
41059243Sobrien	for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
41159243Sobrien	    if (wp->who_status & ANNOUNCE ||
41259243Sobrien		(!eq(STRany, vp[0]) &&
41359243Sobrien		 !Gmatch(str2short(wp->who_name), vp[0]) &&
41459243Sobrien		 !Gmatch(str2short(wp->who_new),  vp[0])) ||
41559243Sobrien		(!Gmatch(str2short(wp->who_tty),  vp[1]) &&
41659243Sobrien		 !eq(STRany, vp[1])))
41759243Sobrien		continue;	/* entry doesn't qualify */
41859243Sobrien	    /* already printed or not right one to print */
41959243Sobrien
42059243Sobrien
42159243Sobrien	    if (wp->who_time == 0)/* utmp entry was cleared */
42259243Sobrien		wp->who_time = watch_period;
42359243Sobrien
42459243Sobrien	    if ((wp->who_status & OFFLINE) &&
42559243Sobrien		(wp->who_name[0] != '\0')) {
42659243Sobrien		if (!firsttime)
42759243Sobrien		    print_who(wp);
42859243Sobrien		wp->who_name[0] = '\0';
42959243Sobrien		wp->who_status |= ANNOUNCE;
43059243Sobrien		continue;
43159243Sobrien	    }
43259243Sobrien	    if (wp->who_status & ONLINE) {
43359243Sobrien		if (!firsttime)
43459243Sobrien		    print_who(wp);
43559243Sobrien		(void) strcpy(wp->who_name, wp->who_new);
43659243Sobrien		wp->who_status |= ANNOUNCE;
43759243Sobrien		continue;
43859243Sobrien	    }
43959243Sobrien	    if (wp->who_status & CHANGED) {
44059243Sobrien		if (!firsttime)
44159243Sobrien		    print_who(wp);
44259243Sobrien		(void) strcpy(wp->who_name, wp->who_new);
44359243Sobrien		wp->who_status |= ANNOUNCE;
44459243Sobrien		continue;
44559243Sobrien	    }
44659243Sobrien	}
44759243Sobrien    }
44859243Sobrien#ifdef BSDSIGS
44959243Sobrien    (void) sigsetmask(omask);
45059243Sobrien#else
45159243Sobrien    (void) sigrelse(SIGINT);
45259243Sobrien#endif
45359243Sobrien}
45459243Sobrien
45559243Sobrien#ifdef WHODEBUG
45659243Sobrienstatic void
45759243Sobriendebugwholist(new, wp)
45859243Sobrien    register struct who *new, *wp;
45959243Sobrien{
46059243Sobrien    register struct who *a;
46159243Sobrien
46259243Sobrien    a = whohead.who_next;
46359243Sobrien    while (a->who_next != NULL) {
46459243Sobrien	xprintf("%s/%s -> ", a->who_name, a->who_tty);
46559243Sobrien	a = a->who_next;
46659243Sobrien    }
46759243Sobrien    xprintf("TAIL\n");
46859243Sobrien    if (a != &whotail) {
46959243Sobrien	xprintf(CGETS(26, 3, "BUG! last element is not whotail!\n"));
47059243Sobrien	abort();
47159243Sobrien    }
47259243Sobrien    a = whotail.who_prev;
47359243Sobrien    xprintf(CGETS(26, 4, "backward: "));
47459243Sobrien    while (a->who_prev != NULL) {
47559243Sobrien	xprintf("%s/%s -> ", a->who_name, a->who_tty);
47659243Sobrien	a = a->who_prev;
47759243Sobrien    }
47859243Sobrien    xprintf("HEAD\n");
47959243Sobrien    if (a != &whohead) {
48059243Sobrien	xprintf(CGETS(26, 5, "BUG! first element is not whohead!\n"));
48159243Sobrien	abort();
48259243Sobrien    }
48359243Sobrien    if (new)
48459243Sobrien	xprintf(CGETS(26, 6, "new: %s/%s\n"), new->who_name, new->who_tty);
48559243Sobrien    if (wp)
48659243Sobrien	xprintf("wp: %s/%s\n", wp->who_name, wp->who_tty);
48759243Sobrien}
48859243Sobrien#endif /* WHODEBUG */
48959243Sobrien
49059243Sobrien
49159243Sobrienstatic void
49259243Sobrienprint_who(wp)
49359243Sobrien    struct who *wp;
49459243Sobrien{
49559243Sobrien#ifdef UTHOST
49659243Sobrien    Char   *cp = str2short(CGETS(26, 7, "%n has %a %l from %m."));
49759243Sobrien#else
49859243Sobrien    Char   *cp = str2short(CGETS(26, 8, "%n has %a %l."));
49959243Sobrien#endif /* UTHOST */
50059243Sobrien    struct varent *vp = adrof(STRwho);
50159243Sobrien    Char buf[BUFSIZE];
50259243Sobrien
50359243Sobrien    if (vp && vp->vec[0])
50459243Sobrien	cp = vp->vec[0];
50559243Sobrien
50659243Sobrien    tprintf(FMT_WHO, buf, cp, BUFSIZE, NULL, wp->who_time, (ptr_t) wp);
50759243Sobrien    for (cp = buf; *cp;)
50859243Sobrien	xputchar(*cp++);
50959243Sobrien    xputchar('\n');
51059243Sobrien} /* end print_who */
51159243Sobrien
51259243Sobrien
51359243Sobrienconst char *
51459243Sobrienwho_info(ptr, c, wbuf, wbufsiz)
51559243Sobrien    ptr_t ptr;
51659243Sobrien    int c;
51759243Sobrien    char *wbuf;
51859243Sobrien    size_t wbufsiz;
51959243Sobrien{
52059243Sobrien    struct who *wp = (struct who *) ptr;
52159243Sobrien#ifdef UTHOST
52259243Sobrien    char *wb = wbuf;
52359243Sobrien    int flg;
52459243Sobrien    char *pb;
52559243Sobrien#endif /* UTHOST */
52659243Sobrien
52759243Sobrien    switch (c) {
52859243Sobrien    case 'n':		/* user name */
52959243Sobrien	switch (wp->who_status & STMASK) {
53059243Sobrien	case ONLINE:
53159243Sobrien	case CHANGED:
53259243Sobrien	    return wp->who_new;
53359243Sobrien	case OFFLINE:
53459243Sobrien	    return wp->who_name;
53559243Sobrien	default:
53659243Sobrien	    break;
53759243Sobrien	}
53859243Sobrien	break;
53959243Sobrien
54059243Sobrien    case 'a':
54159243Sobrien	switch (wp->who_status & STMASK) {
54259243Sobrien	case ONLINE:
54359243Sobrien	    return CGETS(26, 9, "logged on");
54459243Sobrien	case OFFLINE:
54559243Sobrien	    return CGETS(26, 10, "logged off");
54659243Sobrien	case CHANGED:
54759243Sobrien	    xsnprintf(wbuf, wbufsiz, CGETS(26, 11, "replaced %s on"),
54859243Sobrien		      wp->who_name);
54959243Sobrien	    return wbuf;
55059243Sobrien	default:
55159243Sobrien	    break;
55259243Sobrien	}
55359243Sobrien	break;
55459243Sobrien
55559243Sobrien#ifdef UTHOST
55659243Sobrien    case 'm':
55759243Sobrien	if (wp->who_host[0] == '\0')
55859243Sobrien	    return CGETS(26, 12, "local");
55959243Sobrien	else {
56059243Sobrien	    /* the ':' stuff is for <host>:<display>.<screen> */
56159243Sobrien	    for (pb = wp->who_host, flg = Isdigit(*pb) ? '\0' : '.';
56259243Sobrien		 *pb != '\0' &&
56359243Sobrien		 (*pb != flg || ((pb = strchr(pb, ':')) != 0));
56459243Sobrien		 pb++) {
56559243Sobrien		if (*pb == ':')
56659243Sobrien		    flg = '\0';
56759243Sobrien		*wb++ = Isupper(*pb) ? Tolower(*pb) : *pb;
56859243Sobrien	    }
56959243Sobrien	    *wb = '\0';
57059243Sobrien	    return wbuf;
57159243Sobrien	}
57259243Sobrien
57359243Sobrien    case 'M':
57459243Sobrien	if (wp->who_host[0] == '\0')
57559243Sobrien	    return CGETS(26, 12, "local");
57659243Sobrien	else {
57759243Sobrien	    for (pb = wp->who_host; *pb != '\0'; pb++)
57859243Sobrien		*wb++ = Isupper(*pb) ? Tolower(*pb) : *pb;
57959243Sobrien	    *wb = '\0';
58059243Sobrien	    return wbuf;
58159243Sobrien	}
58259243Sobrien#endif /* UTHOST */
58359243Sobrien
58459243Sobrien    case 'l':
58559243Sobrien	return wp->who_tty;
58659243Sobrien
58759243Sobrien    default:
58859243Sobrien	wbuf[0] = '%';
58959243Sobrien	wbuf[1] = (char) c;
59059243Sobrien	wbuf[2] = '\0';
59159243Sobrien	return wbuf;
59259243Sobrien    }
59359243Sobrien    return NULL;
59459243Sobrien}
59559243Sobrien
59659243Sobrienvoid
59759243Sobrien/*ARGSUSED*/
59859243Sobriendolog(v, c)
59959243SobrienChar **v;
60059243Sobrienstruct command *c;
60159243Sobrien{
60259243Sobrien    struct who *wp;
60359243Sobrien    struct varent *vp;
60459243Sobrien
60559243Sobrien    USE(v);
60659243Sobrien    USE(c);
60759243Sobrien    vp = adrof(STRwatch);	/* lint insists vp isn't used unless we */
60859243Sobrien    if (vp == NULL)		/* unless we assign it outside the if */
60959243Sobrien	stderror(ERR_NOWATCH);
61059243Sobrien    resetwatch();
61159243Sobrien    wp = whohead.who_next;
61259243Sobrien    while (wp->who_next != NULL) {
61359243Sobrien	wp->who_name[0] = '\0';
61459243Sobrien	wp = wp->who_next;
61559243Sobrien    }
61659243Sobrien}
61759243Sobrien
61859243Sobrien# ifdef UTHOST
61969408Sachesize_t
62069408Sacheutmphostsize()
62169408Sache{
62269408Sache    return UTHOSTLEN;
62369408Sache}
62469408Sache
62559243Sobrienchar *
62659243Sobrienutmphost()
62759243Sobrien{
62859243Sobrien    char *tty = short2str(varval(STRtty));
62959243Sobrien    struct who *wp;
63059243Sobrien    char *host = NULL;
63159243Sobrien
63259243Sobrien    watch_login(1);
63359243Sobrien
63459243Sobrien    for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
63559243Sobrien	if (strcmp(tty, wp->who_tty) == 0)
63659243Sobrien	    host = wp->who_host;
63759243Sobrien	wp->who_name[0] = '\0';
63859243Sobrien    }
63959243Sobrien    resetwatch();
64059243Sobrien    return host;
64159243Sobrien}
64259243Sobrien# endif /* UTHOST */
64359243Sobrien
64469408Sache#ifdef WINNT_NATIVE
64559243Sobrienvoid add_to_who_list(name, mach_nm)
64659243Sobrien    char *name;
64759243Sobrien    char *mach_nm;
64859243Sobrien{
64959243Sobrien
65059243Sobrien    struct who *wp, *wpnew;
65159243Sobrien    int comp = -1;
65259243Sobrien
65359243Sobrien    wp = whohead.who_next;
65459243Sobrien    while (wp->who_next && (comp = strncmp(wp->who_tty,mach_nm,UTLINLEN)) < 0)
65559243Sobrien	wp = wp->who_next;/* find that tty! */
65659243Sobrien
65759243Sobrien    if (wp->who_next && comp == 0) {	/* found the tty... */
65859243Sobrien
65959243Sobrien	if (*name == '\0') {
66059243Sobrien	    wp->who_time = 0;
66159243Sobrien	    wp->who_status = OFFLINE;
66259243Sobrien	}
66359243Sobrien	else if (strncmp(name, wp->who_name, UTNAMLEN) == 0) {
66459243Sobrien	    /* someone is logged in */
66559243Sobrien	    wp->who_time = 0;
66659243Sobrien	    wp->who_status = 0;	/* same guy */
66759243Sobrien	}
66859243Sobrien	else {
66959243Sobrien	    (void) strncpy(wp->who_new, name, UTNAMLEN);
67059243Sobrien	    wp->who_time = 0;
67159243Sobrien	    if (wp->who_name[0] == '\0')
67259243Sobrien		wp->who_status = ONLINE;
67359243Sobrien	    else
67459243Sobrien		wp->who_status = CHANGED;
67559243Sobrien	}
67659243Sobrien    }
67759243Sobrien    else {
67859243Sobrien	wpnew = (struct who *) xcalloc(1, sizeof *wpnew);
67959243Sobrien	(void) strncpy(wpnew->who_tty, mach_nm, UTLINLEN);
68059243Sobrien	wpnew->who_time = 0;
68159243Sobrien	if (*name == '\0')
68259243Sobrien	    wpnew->who_status = OFFLINE;
68359243Sobrien	else {
68459243Sobrien	    (void) strncpy(wpnew->who_new, name, UTNAMLEN);
68559243Sobrien	    wpnew->who_status = ONLINE;
68659243Sobrien	}
68759243Sobrien#ifdef WHODEBUG
68859243Sobrien	debugwholist(wpnew, wp);
68959243Sobrien#endif /* WHODEBUG */
69059243Sobrien
69159243Sobrien	wpnew->who_next = wp;	/* link in a new 'who' */
69259243Sobrien	wpnew->who_prev = wp->who_prev;
69359243Sobrien	wpnew->who_prev->who_next = wpnew;
69459243Sobrien	wp->who_prev = wpnew;	/* linked in now */
69559243Sobrien    }
69659243Sobrien}
69769408Sache#endif /* WINNT_NATIVE */
69859243Sobrien#endif /* HAVENOUTMP */
699