1/* $Header: /p/tcsh/cvsroot/tcsh/tc.who.c,v 3.51 2006/03/03 22:08:45 amold Exp $ */
2/*
3 * tc.who.c: Watch logins and logouts...
4 */
5/*-
6 * Copyright (c) 1980, 1991 The Regents of the University of California.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33#include "sh.h"
34
35RCSID("$tcsh: tc.who.c,v 3.51 2006/03/03 22:08:45 amold Exp $")
36
37#include "tc.h"
38
39#ifndef HAVENOUTMP
40/*
41 * kfk 26 Jan 1984 - for login watch functions.
42 */
43#include <ctype.h>
44
45#ifdef HAVE_UTMPX_H
46# include <utmpx.h>
47/* I just redefine a few words here.  Changing every occurrence below
48 * seems like too much of work.  All UTMP functions have equivalent
49 * UTMPX counterparts, so they can be added all here when needed.
50 * Kimmo Suominen, Oct 14 1991
51 */
52# if defined(__UTMPX_FILE) && !defined(UTMPX_FILE)
53#  define TCSH_PATH_UTMP __UTMPX_FILE
54# elif defined(_PATH_UTMPX)
55#  define TCSH_PATH_UTMP _PATH_UTMPX
56# elif defined(UTMPX_FILE)
57#  define TCSH_PATH_UTMP UTMPX_FILE
58# endif /* __UTMPX_FILE && !UTMPX_FILE */
59# ifdef TCSH_PATH_UTMP
60#  define utmp utmpx
61#  if defined(HAVE_STRUCT_UTMP_UT_TV)
62#   define ut_time ut_tv.tv_sec
63#  elif defined(HAVE_STRUCT_UTMP_UT_XTIME)
64#   define ut_time ut_xtime
65#  endif
66#  ifdef HAVE_STRUCT_UTMP_UT_USER
67#   define ut_name ut_user
68#  endif
69#  ifdef HAVE_GETUTENT
70#   define getutent getutxent
71#   define setutent setutxent
72#   define endutent endutxent
73#  endif /* HAVE_GETUTENT */
74# else
75#  ifdef HAVE_UTMP_H
76#   include <utmp.h>
77#  endif /* WINNT_NATIVE */
78# endif /* TCSH_PATH_UTMP */
79#else /* !HAVE_UTMPX_H */
80# ifdef HAVE_UTMP_H
81#  include <utmp.h>
82# endif /* WINNT_NATIVE */
83#endif /* HAVE_UTMPX_H */
84
85#ifndef BROKEN_CC
86# define UTNAMLEN	sizeof(((struct utmp *) 0)->ut_name)
87# define UTLINLEN	sizeof(((struct utmp *) 0)->ut_line)
88# ifdef HAVE_STRUCT_UTMP_UT_HOST
89#  ifdef _SEQUENT_
90#   define UTHOSTLEN	100
91#  else
92#   define UTHOSTLEN	sizeof(((struct utmp *) 0)->ut_host)
93#  endif
94# endif	/* HAVE_STRUCT_UTMP_UT_HOST */
95#else
96/* give poor cc a little help if it needs it */
97struct utmp __ut;
98
99# define UTNAMLEN	sizeof(__ut.ut_name)
100# define UTLINLEN	sizeof(__ut.ut_line)
101# ifdef HAVE_STRUCT_UTMP_UT_HOST
102#  ifdef _SEQUENT_
103#   define UTHOSTLEN	100
104#  else
105#   define UTHOSTLEN	sizeof(__ut.ut_host)
106#  endif
107# endif /* HAVE_STRUCT_UTMP_UT_HOST */
108#endif /* BROKEN_CC */
109
110#ifndef TCSH_PATH_UTMP
111# ifdef	UTMP_FILE
112#  define TCSH_PATH_UTMP UTMP_FILE
113# elif defined(_PATH_UTMP)
114#  define TCSH_PATH_UTMP _PATH_UTMP
115# else
116#  define TCSH_PATH_UTMP "/etc/utmp"
117# endif /* UTMP_FILE */
118#endif /* TCSH_PATH_UTMP */
119
120
121struct who {
122    struct who *who_next;
123    struct who *who_prev;
124    char    who_name[UTNAMLEN + 1];
125    char    who_new[UTNAMLEN + 1];
126    char    who_tty[UTLINLEN + 1];
127#ifdef HAVE_STRUCT_UTMP_UT_HOST
128    char    who_host[UTHOSTLEN + 1];
129#endif /* HAVE_STRUCT_UTMP_UT_HOST */
130    time_t  who_time;
131    int     who_status;
132};
133
134static struct who whohead, whotail;
135static time_t watch_period = 0;
136static time_t stlast = 0;
137#ifdef WHODEBUG
138static	void	debugwholist	(struct who *, struct who *);
139#endif
140static	void	print_who	(struct who *);
141
142
143#define ONLINE		01
144#define OFFLINE		02
145#define CHANGED		04
146#define STMASK		07
147#define ANNOUNCE	010
148#define CLEARED		020
149
150/*
151 * Karl Kleinpaste, 26 Jan 1984.
152 * Initialize the dummy tty list for login watch.
153 * This dummy list eliminates boundary conditions
154 * when doing pointer-chase searches.
155 */
156void
157initwatch(void)
158{
159    whohead.who_next = &whotail;
160    whotail.who_prev = &whohead;
161    stlast = 1;
162#ifdef WHODEBUG
163    debugwholist(NULL, NULL);
164#endif /* WHODEBUG */
165}
166
167void
168resetwatch(void)
169{
170    watch_period = 0;
171    stlast = 0;
172}
173
174/*
175 * Karl Kleinpaste, 26 Jan 1984.
176 * Watch /etc/utmp for login/logout changes.
177 */
178void
179watch_login(int force)
180{
181    int     comp = -1, alldone;
182    int	    firsttime = stlast == 1;
183#ifdef HAVE_GETUTENT
184    struct utmp *uptr;
185#else
186    int utmpfd;
187#endif
188    struct utmp utmp;
189    struct who *wp, *wpnew;
190    struct varent *v;
191    Char  **vp = NULL;
192    time_t  t, interval = MAILINTVL;
193    struct stat sta;
194#if defined(HAVE_STRUCT_UTMP_UT_HOST) && defined(_SEQUENT_)
195    char   *host, *ut_find_host();
196#endif
197#ifdef WINNT_NATIVE
198    static int ncbs_posted = 0;
199    USE(utmp);
200    USE(utmpfd);
201    USE(sta);
202    USE(wpnew);
203#endif /* WINNT_NATIVE */
204
205    /* stop SIGINT, lest our login list get trashed. */
206    pintr_disabled++;
207    cleanup_push(&pintr_disabled, disabled_cleanup);
208
209    v = adrof(STRwatch);
210    if ((v == NULL || v->vec == NULL) && !force) {
211	cleanup_until(&pintr_disabled);
212	return;			/* no names to watch */
213    }
214    if (!force) {
215	trim(vp = v->vec);
216	if (blklen(vp) % 2)		/* odd # args: 1st == # minutes. */
217	    interval = (number(*vp)) ? (getn(*vp++) * 60) : MAILINTVL;
218    }
219    else
220	interval = 0;
221
222    (void) time(&t);
223#ifdef WINNT_NATIVE
224	/*
225	 * Since NCB_ASTATs take time, start em async at least 90 secs
226	 * before we are due -amol 6/5/97
227	 */
228	if (!ncbs_posted) {
229	    time_t tdiff = t - watch_period;
230	    if (!watch_period || ((tdiff  > 0) && (tdiff > (interval - 90)))) {
231		start_ncbs(vp);
232 		ncbs_posted = 1;
233	    }
234	}
235#endif /* WINNT_NATIVE */
236    if (t - watch_period < interval) {
237	cleanup_until(&pintr_disabled);
238	return;			/* not long enough yet... */
239    }
240    watch_period = t;
241#ifdef WINNT_NATIVE
242    ncbs_posted = 0;
243#else /* !WINNT_NATIVE */
244
245    /*
246     * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de>
247     * Don't open utmp all the time, stat it first...
248     */
249    if (stat(TCSH_PATH_UTMP, &sta)) {
250	if (!force)
251	    xprintf(CGETS(26, 1,
252			  "cannot stat %s.  Please \"unset watch\".\n"),
253		    TCSH_PATH_UTMP);
254	cleanup_until(&pintr_disabled);
255	return;
256    }
257    if (stlast == sta.st_mtime) {
258	cleanup_until(&pintr_disabled);
259	return;
260    }
261    stlast = sta.st_mtime;
262#ifdef HAVE_GETUTENT
263    setutent();
264#else
265    if ((utmpfd = xopen(TCSH_PATH_UTMP, O_RDONLY|O_LARGEFILE)) < 0) {
266	if (!force)
267	    xprintf(CGETS(26, 2,
268			  "%s cannot be opened.  Please \"unset watch\".\n"),
269		    TCSH_PATH_UTMP);
270	cleanup_until(&pintr_disabled);
271	return;
272    }
273    cleanup_push(&utmpfd, open_cleanup);
274#endif
275
276    /*
277     * xterm clears the entire utmp entry - mark everyone on the status list
278     * OFFLINE or we won't notice X "logouts"
279     */
280    for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next)
281	wp->who_status = OFFLINE | CLEARED;
282
283    /*
284     * Read in the utmp file, sort the entries, and update existing entries or
285     * add new entries to the status list.
286     */
287#ifdef HAVE_GETUTENT
288    while ((uptr = getutent()) != NULL) {
289        memcpy(&utmp, uptr, sizeof (utmp));
290#else
291    while (xread(utmpfd, &utmp, sizeof utmp) == sizeof utmp) {
292#endif
293
294# ifdef DEAD_PROCESS
295#  ifndef IRIS4D
296	if (utmp.ut_type != USER_PROCESS)
297	    continue;
298#  else
299	/* Why is that? Cause the utmp file is always corrupted??? */
300	if (utmp.ut_type != USER_PROCESS && utmp.ut_type != DEAD_PROCESS)
301	    continue;
302#  endif /* IRIS4D */
303# endif /* DEAD_PROCESS */
304
305	if (utmp.ut_name[0] == '\0' && utmp.ut_line[0] == '\0')
306	    continue;	/* completely void entry */
307# ifdef DEAD_PROCESS
308	if (utmp.ut_type == DEAD_PROCESS && utmp.ut_line[0] == '\0')
309	    continue;
310# endif /* DEAD_PROCESS */
311	wp = whohead.who_next;
312	while (wp->who_next && (comp = strncmp(wp->who_tty, utmp.ut_line, UTLINLEN)) < 0)
313	    wp = wp->who_next;/* find that tty! */
314
315	if (wp->who_next && comp == 0) {	/* found the tty... */
316	    if (utmp.ut_time < wp->who_time)
317	        continue;
318# ifdef DEAD_PROCESS
319	    if (utmp.ut_type == DEAD_PROCESS) {
320		wp->who_time = utmp.ut_time;
321		wp->who_status = OFFLINE;
322	    }
323	    else
324# endif /* DEAD_PROCESS */
325	    if (utmp.ut_name[0] == '\0') {
326		wp->who_time = utmp.ut_time;
327		wp->who_status = OFFLINE;
328	    }
329	    else if (strncmp(utmp.ut_name, wp->who_name, UTNAMLEN) == 0) {
330		/* someone is logged in */
331		wp->who_time = utmp.ut_time;
332		wp->who_status = ONLINE | ANNOUNCE;	/* same guy */
333	    }
334	    else {
335		(void) strncpy(wp->who_new, utmp.ut_name, UTNAMLEN);
336# ifdef HAVE_STRUCT_UTMP_UT_HOST
337#  ifdef _SEQUENT_
338		host = ut_find_host(wp->who_tty);
339		if (host)
340		    (void) strncpy(wp->who_host, host, UTHOSTLEN);
341		else
342		    wp->who_host[0] = 0;
343#  else
344		(void) strncpy(wp->who_host, utmp.ut_host, UTHOSTLEN);
345#  endif
346# endif /* HAVE_STRUCT_UTMP_UT_HOST */
347		wp->who_time = utmp.ut_time;
348		if (wp->who_name[0] == '\0')
349		    wp->who_status = ONLINE;
350		else
351		    wp->who_status = CHANGED;
352	    }
353	}
354	else {		/* new tty in utmp */
355	    wpnew = xcalloc(1, sizeof *wpnew);
356	    (void) strncpy(wpnew->who_tty, utmp.ut_line, UTLINLEN);
357# ifdef HAVE_STRUCT_UTMP_UT_HOST
358#  ifdef _SEQUENT_
359	    host = ut_find_host(wpnew->who_tty);
360	    if (host)
361		(void) strncpy(wpnew->who_host, host, UTHOSTLEN);
362	    else
363		wpnew->who_host[0] = 0;
364#  else
365	    (void) strncpy(wpnew->who_host, utmp.ut_host, UTHOSTLEN);
366#  endif
367# endif /* HAVE_STRUCT_UTMP_UT_HOST */
368	    wpnew->who_time = utmp.ut_time;
369# ifdef DEAD_PROCESS
370	    if (utmp.ut_type == DEAD_PROCESS)
371		wpnew->who_status = OFFLINE;
372	    else
373# endif /* DEAD_PROCESS */
374	    if (utmp.ut_name[0] == '\0')
375		wpnew->who_status = OFFLINE;
376	    else {
377		(void) strncpy(wpnew->who_new, utmp.ut_name, UTNAMLEN);
378		wpnew->who_status = ONLINE;
379	    }
380# ifdef WHODEBUG
381	    debugwholist(wpnew, wp);
382# endif /* WHODEBUG */
383
384	    wpnew->who_next = wp;	/* link in a new 'who' */
385	    wpnew->who_prev = wp->who_prev;
386	    wpnew->who_prev->who_next = wpnew;
387	    wp->who_prev = wpnew;	/* linked in now */
388	}
389    }
390#ifdef HAVE_GETUTENT
391    endutent();
392#else
393    cleanup_until(&utmpfd);
394#endif
395# if defined(HAVE_STRUCT_UTMP_UT_HOST) && defined(_SEQUENT_)
396    endutent();
397# endif
398#endif /* !WINNT_NATIVE */
399
400    if (force || vp == NULL) {
401	cleanup_until(&pintr_disabled);
402	return;
403    }
404
405    /*
406     * The state of all logins is now known, so we can search the user's list
407     * of watchables to print the interesting ones.
408     */
409    for (alldone = 0; !alldone && *vp != NULL && **vp != '\0' &&
410	 *(vp + 1) != NULL && **(vp + 1) != '\0';
411	 vp += 2) {		/* args used in pairs... */
412
413	if (eq(*vp, STRany) && eq(*(vp + 1), STRany))
414	    alldone = 1;
415
416	for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
417	    if (wp->who_status & ANNOUNCE ||
418		(!eq(STRany, vp[0]) &&
419		 !Gmatch(str2short(wp->who_name), vp[0]) &&
420		 !Gmatch(str2short(wp->who_new),  vp[0])) ||
421		(!Gmatch(str2short(wp->who_tty),  vp[1]) &&
422		 !eq(STRany, vp[1])))
423		continue;	/* entry doesn't qualify */
424	    /* already printed or not right one to print */
425
426
427	    if (wp->who_status & CLEARED) {/* utmp entry was cleared */
428		wp->who_time = watch_period;
429		wp->who_status &= ~CLEARED;
430	    }
431
432	    if ((wp->who_status & OFFLINE) &&
433		(wp->who_name[0] != '\0')) {
434		if (!firsttime)
435		    print_who(wp);
436		wp->who_name[0] = '\0';
437		wp->who_status |= ANNOUNCE;
438		continue;
439	    }
440	    if (wp->who_status & ONLINE) {
441		if (!firsttime)
442		    print_who(wp);
443		(void) strcpy(wp->who_name, wp->who_new);
444		wp->who_status |= ANNOUNCE;
445		continue;
446	    }
447	    if (wp->who_status & CHANGED) {
448		if (!firsttime)
449		    print_who(wp);
450		(void) strcpy(wp->who_name, wp->who_new);
451		wp->who_status |= ANNOUNCE;
452		continue;
453	    }
454	}
455    }
456    cleanup_until(&pintr_disabled);
457}
458
459#ifdef WHODEBUG
460static void
461debugwholist(struct who *new, struct who *wp)
462{
463    struct who *a;
464
465    a = whohead.who_next;
466    while (a->who_next != NULL) {
467	xprintf("%s/%s -> ", a->who_name, a->who_tty);
468	a = a->who_next;
469    }
470    xprintf("TAIL\n");
471    if (a != &whotail) {
472	xprintf(CGETS(26, 3, "BUG! last element is not whotail!\n"));
473	abort();
474    }
475    a = whotail.who_prev;
476    xprintf(CGETS(26, 4, "backward: "));
477    while (a->who_prev != NULL) {
478	xprintf("%s/%s -> ", a->who_name, a->who_tty);
479	a = a->who_prev;
480    }
481    xprintf("HEAD\n");
482    if (a != &whohead) {
483	xprintf(CGETS(26, 5, "BUG! first element is not whohead!\n"));
484	abort();
485    }
486    if (new)
487	xprintf(CGETS(26, 6, "new: %s/%s\n"), new->who_name, new->who_tty);
488    if (wp)
489	xprintf("wp: %s/%s\n", wp->who_name, wp->who_tty);
490}
491#endif /* WHODEBUG */
492
493
494static void
495print_who(struct who *wp)
496{
497#ifdef HAVE_STRUCT_UTMP_UT_HOST
498    Char   *cp = str2short(CGETS(26, 7, "%n has %a %l from %m."));
499#else
500    Char   *cp = str2short(CGETS(26, 8, "%n has %a %l."));
501#endif /* HAVE_STRUCT_UTMP_UT_HOST */
502    struct varent *vp = adrof(STRwho);
503    Char *str;
504
505    if (vp && vp->vec && vp->vec[0])
506	cp = vp->vec[0];
507
508    str = tprintf(FMT_WHO, cp, NULL, wp->who_time, wp);
509    cleanup_push(str, xfree);
510    for (cp = str; *cp;)
511	xputwchar(*cp++);
512    cleanup_until(str);
513    xputchar('\n');
514} /* end print_who */
515
516
517char *
518who_info(ptr_t ptr, int c)
519{
520    struct who *wp = ptr;
521    char *wbuf;
522#ifdef HAVE_STRUCT_UTMP_UT_HOST
523    char *wb;
524    int flg;
525    char *pb;
526#endif /* HAVE_STRUCT_UTMP_UT_HOST */
527
528    switch (c) {
529    case 'n':		/* user name */
530	switch (wp->who_status & STMASK) {
531	case ONLINE:
532	case CHANGED:
533	    return strsave(wp->who_new);
534	case OFFLINE:
535	    return strsave(wp->who_name);
536	default:
537	    break;
538	}
539	break;
540
541    case 'a':
542	switch (wp->who_status & STMASK) {
543	case ONLINE:
544	    return strsave(CGETS(26, 9, "logged on"));
545	case OFFLINE:
546	    return strsave(CGETS(26, 10, "logged off"));
547	case CHANGED:
548	    return xasprintf(CGETS(26, 11, "replaced %s on"), wp->who_name);
549	default:
550	    break;
551	}
552	break;
553
554#ifdef HAVE_STRUCT_UTMP_UT_HOST
555    case 'm':
556	if (wp->who_host[0] == '\0')
557	    return strsave(CGETS(26, 12, "local"));
558	else {
559	    pb = wp->who_host;
560	    wbuf = xmalloc(strlen(pb) + 1);
561	    wb = wbuf;
562	    /* the ':' stuff is for <host>:<display>.<screen> */
563	    for (flg = isdigit((unsigned char)*pb) ? '\0' : '.';
564		 *pb != '\0' && (*pb != flg || ((pb = strchr(pb, ':')) != 0));
565		 pb++) {
566		if (*pb == ':')
567		    flg = '\0';
568		*wb++ = isupper((unsigned char)*pb) ?
569		    tolower((unsigned char)*pb) : *pb;
570	    }
571	    *wb = '\0';
572	    return wbuf;
573	}
574
575    case 'M':
576	if (wp->who_host[0] == '\0')
577	    return strsave(CGETS(26, 12, "local"));
578	else {
579	    pb = wp->who_host;
580	    wbuf = xmalloc(strlen(pb) + 1);
581	    wb = wbuf;
582	    for (; *pb != '\0'; pb++)
583		*wb++ = isupper((unsigned char)*pb) ?
584		    tolower((unsigned char)*pb) : *pb;
585	    *wb = '\0';
586	    return wbuf;
587	}
588#endif /* HAVE_STRUCT_UTMP_UT_HOST */
589
590    case 'l':
591	return strsave(wp->who_tty);
592
593    default:
594	wbuf = xmalloc(3);
595	wbuf[0] = '%';
596	wbuf[1] = (char) c;
597	wbuf[2] = '\0';
598	return wbuf;
599    }
600    return NULL;
601}
602
603void
604/*ARGSUSED*/
605dolog(Char **v, struct command *c)
606{
607    struct who *wp;
608    struct varent *vp;
609
610    USE(v);
611    USE(c);
612    vp = adrof(STRwatch);	/* lint insists vp isn't used unless we */
613    if (vp == NULL)		/* unless we assign it outside the if */
614	stderror(ERR_NOWATCH);
615    resetwatch();
616    wp = whohead.who_next;
617    while (wp->who_next != NULL) {
618	wp->who_name[0] = '\0';
619	wp = wp->who_next;
620    }
621}
622
623# ifdef HAVE_STRUCT_UTMP_UT_HOST
624size_t
625utmphostsize(void)
626{
627    return UTHOSTLEN;
628}
629
630char *
631utmphost(void)
632{
633    char *tty = short2str(varval(STRtty));
634    struct who *wp;
635    char *host = NULL;
636
637    watch_login(1);
638
639    for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
640	if (strcmp(tty, wp->who_tty) == 0)
641	    host = wp->who_host;
642	wp->who_name[0] = '\0';
643    }
644    resetwatch();
645    return host;
646}
647# endif /* HAVE_STRUCT_UTMP_UT_HOST */
648
649#ifdef WINNT_NATIVE
650void add_to_who_list(name, mach_nm)
651    char *name;
652    char *mach_nm;
653{
654
655    struct who *wp, *wpnew;
656    int comp = -1;
657
658    wp = whohead.who_next;
659    while (wp->who_next && (comp = strncmp(wp->who_tty,mach_nm,UTLINLEN)) < 0)
660	wp = wp->who_next;/* find that tty! */
661
662    if (wp->who_next && comp == 0) {	/* found the tty... */
663
664	if (*name == '\0') {
665	    wp->who_time = 0;
666	    wp->who_status = OFFLINE;
667	}
668	else if (strncmp(name, wp->who_name, UTNAMLEN) == 0) {
669	    /* someone is logged in */
670	    wp->who_time = 0;
671	    wp->who_status = 0;	/* same guy */
672	}
673	else {
674	    (void) strncpy(wp->who_new, name, UTNAMLEN);
675	    wp->who_time = 0;
676	    if (wp->who_name[0] == '\0')
677		wp->who_status = ONLINE;
678	    else
679		wp->who_status = CHANGED;
680	}
681    }
682    else {
683	wpnew = xcalloc(1, sizeof *wpnew);
684	(void) strncpy(wpnew->who_tty, mach_nm, UTLINLEN);
685	wpnew->who_time = 0;
686	if (*name == '\0')
687	    wpnew->who_status = OFFLINE;
688	else {
689	    (void) strncpy(wpnew->who_new, name, UTNAMLEN);
690	    wpnew->who_status = ONLINE;
691	}
692#ifdef WHODEBUG
693	debugwholist(wpnew, wp);
694#endif /* WHODEBUG */
695
696	wpnew->who_next = wp;	/* link in a new 'who' */
697	wpnew->who_prev = wp->who_prev;
698	wpnew->who_prev->who_next = wpnew;
699	wp->who_prev = wpnew;	/* linked in now */
700    }
701}
702#endif /* WINNT_NATIVE */
703#endif /* HAVENOUTMP */
704