]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/tcsh/tc.who.c
This commit was generated by cvs2svn to compensate for changes in r153816,
[FreeBSD/FreeBSD.git] / contrib / tcsh / tc.who.c
1 /* $Header: /src/pub/tcsh/tc.who.c,v 3.44 2005/03/03 23:44:45 kim 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
35 RCSID("$Id: tc.who.c,v 3.44 2005/03/03 23:44:45 kim 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 */
97 struct 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
121 struct 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
134 static struct who whohead, whotail;
135 static time_t watch_period = 0;
136 static time_t stlast = 0;
137 #ifdef WHODEBUG
138 static  void    debugwholist    __P((struct who *, struct who *));
139 #endif
140 static  void    print_who       __P((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
149 /*
150  * Karl Kleinpaste, 26 Jan 1984.
151  * Initialize the dummy tty list for login watch.
152  * This dummy list eliminates boundary conditions
153  * when doing pointer-chase searches.
154  */
155 void
156 initwatch()
157 {
158     whohead.who_next = &whotail;
159     whotail.who_prev = &whohead;
160     stlast = 1;
161 #ifdef WHODEBUG
162     debugwholist(NULL, NULL);
163 #endif /* WHODEBUG */
164 }
165
166 void
167 resetwatch()
168 {
169     watch_period = 0;
170     stlast = 0;
171 }
172
173 /*
174  * Karl Kleinpaste, 26 Jan 1984.
175  * Watch /etc/utmp for login/logout changes.
176  */
177 void
178 watch_login(force)
179     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 #ifdef BSDSIGS
189     sigmask_t omask;
190 #endif                          /* BSDSIGS */
191     struct utmp utmp;
192     struct who *wp, *wpnew;
193     struct varent *v;
194     Char  **vp = NULL;
195     time_t  t, interval = MAILINTVL;
196     struct stat sta;
197 #if defined(HAVE_STRUCT_UTMP_UT_HOST) && defined(_SEQUENT_)
198     char   *host, *ut_find_host();
199 #endif
200 #ifdef WINNT_NATIVE
201     static int ncbs_posted = 0;
202     USE(utmp);
203     USE(utmpfd);
204     USE(sta);
205     USE(wpnew);
206 #endif /* WINNT_NATIVE */
207
208     /* stop SIGINT, lest our login list get trashed. */
209 #ifdef BSDSIGS
210     omask = sigblock(sigmask(SIGINT));
211 #else
212     (void) sighold(SIGINT);
213 #endif
214
215     v = adrof(STRwatch);
216     if ((v == NULL || v->vec == NULL) && !force) {
217 #ifdef BSDSIGS
218         (void) sigsetmask(omask);
219 #else
220         (void) sigrelse(SIGINT);
221 #endif
222         return;                 /* no names to watch */
223     }
224     if (!force) {
225         trim(vp = v->vec);
226         if (blklen(vp) % 2)             /* odd # args: 1st == # minutes. */
227             interval = (number(*vp)) ? (getn(*vp++) * 60) : MAILINTVL;
228     }
229     else
230         interval = 0;
231         
232     (void) time(&t);
233 #ifdef WINNT_NATIVE
234         /*
235          * Since NCB_ASTATs take time, start em async at least 90 secs
236          * before we are due -amol 6/5/97
237          */
238         if (!ncbs_posted) {
239             unsigned long tdiff = t - watch_period;
240             if (!watch_period || ((tdiff  > 0) && (tdiff > (interval - 90)))) {
241                 start_ncbs(vp);
242                 ncbs_posted = 1;
243             }
244         }
245 #endif /* WINNT_NATIVE */
246     if (t - watch_period < interval) {
247 #ifdef BSDSIGS
248         (void) sigsetmask(omask);
249 #else
250         (void) sigrelse(SIGINT);
251 #endif
252         return;                 /* not long enough yet... */
253     }
254     watch_period = t;
255 #ifdef WINNT_NATIVE
256     ncbs_posted = 0;
257 #else /* !WINNT_NATIVE */
258
259     /*
260      * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de>
261      * Don't open utmp all the time, stat it first...
262      */
263     if (stat(TCSH_PATH_UTMP, &sta)) {
264         if (!force)
265             xprintf(CGETS(26, 1,
266                           "cannot stat %s.  Please \"unset watch\".\n"),
267                     TCSH_PATH_UTMP);
268 # ifdef BSDSIGS
269         (void) sigsetmask(omask);
270 # else
271         (void) sigrelse(SIGINT);
272 # endif
273         return;
274     }
275     if (stlast == sta.st_mtime) {
276 # ifdef BSDSIGS
277         (void) sigsetmask(omask);
278 # else
279         (void) sigrelse(SIGINT);
280 # endif
281         return;
282     }
283     stlast = sta.st_mtime;
284 #ifdef HAVE_GETUTENT
285     setutent();
286 #else
287     if ((utmpfd = open(TCSH_PATH_UTMP, O_RDONLY|O_LARGEFILE)) < 0) {
288         if (!force)
289             xprintf(CGETS(26, 2,
290                           "%s cannot be opened.  Please \"unset watch\".\n"),
291                     TCSH_PATH_UTMP);
292 # ifdef BSDSIGS
293         (void) sigsetmask(omask);
294 # else
295         (void) sigrelse(SIGINT);
296 # endif
297         return;
298     }
299 #endif
300
301     /*
302      * xterm clears the entire utmp entry - mark everyone on the status list
303      * OFFLINE or we won't notice X "logouts"
304      */
305     for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
306         wp->who_status = OFFLINE;
307         wp->who_time = 0;
308     }
309
310     /*
311      * Read in the utmp file, sort the entries, and update existing entries or
312      * add new entries to the status list.
313      */
314 #ifdef HAVE_GETUTENT
315     while ((uptr = getutent()) != NULL) {
316         memcpy(&utmp, uptr, sizeof (utmp));
317 #else
318     while (read(utmpfd, (char *) &utmp, sizeof utmp) == sizeof utmp) {
319 #endif
320
321 # ifdef DEAD_PROCESS
322 #  ifndef IRIS4D
323         if (utmp.ut_type != USER_PROCESS)
324             continue;
325 #  else
326         /* Why is that? Cause the utmp file is always corrupted??? */
327         if (utmp.ut_type != USER_PROCESS && utmp.ut_type != DEAD_PROCESS)
328             continue;
329 #  endif /* IRIS4D */
330 # endif /* DEAD_PROCESS */
331
332         if (utmp.ut_name[0] == '\0' && utmp.ut_line[0] == '\0')
333             continue;   /* completely void entry */
334 # ifdef DEAD_PROCESS
335         if (utmp.ut_type == DEAD_PROCESS && utmp.ut_line[0] == '\0')
336             continue;
337 # endif /* DEAD_PROCESS */
338         wp = whohead.who_next;
339         while (wp->who_next && (comp = strncmp(wp->who_tty, utmp.ut_line, UTLINLEN)) < 0)
340             wp = wp->who_next;/* find that tty! */
341
342         if (wp->who_next && comp == 0) {        /* found the tty... */
343 # ifdef DEAD_PROCESS
344             if (utmp.ut_type == DEAD_PROCESS) {
345                 wp->who_time = utmp.ut_time;
346                 wp->who_status = OFFLINE;
347             }
348             else
349 # endif /* DEAD_PROCESS */
350             if (utmp.ut_name[0] == '\0') {
351                 wp->who_time = utmp.ut_time;
352                 wp->who_status = OFFLINE;
353             }
354             else if (strncmp(utmp.ut_name, wp->who_name, UTNAMLEN) == 0) {
355                 /* someone is logged in */ 
356                 wp->who_time = utmp.ut_time;
357                 wp->who_status = 0;     /* same guy */
358             }
359             else {
360                 (void) strncpy(wp->who_new, utmp.ut_name, UTNAMLEN);
361 # ifdef HAVE_STRUCT_UTMP_UT_HOST
362 #  ifdef _SEQUENT_
363                 host = ut_find_host(wp->who_tty);
364                 if (host)
365                     (void) strncpy(wp->who_host, host, UTHOSTLEN);
366                 else
367                     wp->who_host[0] = 0;
368 #  else
369                 (void) strncpy(wp->who_host, utmp.ut_host, UTHOSTLEN);
370 #  endif
371 # endif /* HAVE_STRUCT_UTMP_UT_HOST */
372                 wp->who_time = utmp.ut_time;
373                 if (wp->who_name[0] == '\0')
374                     wp->who_status = ONLINE;
375                 else
376                     wp->who_status = CHANGED;
377             }
378         }
379         else {          /* new tty in utmp */
380             wpnew = (struct who *) xcalloc(1, sizeof *wpnew);
381             (void) strncpy(wpnew->who_tty, utmp.ut_line, UTLINLEN);
382 # ifdef HAVE_STRUCT_UTMP_UT_HOST
383 #  ifdef _SEQUENT_
384             host = ut_find_host(wpnew->who_tty);
385             if (host)
386                 (void) strncpy(wpnew->who_host, host, UTHOSTLEN);
387             else
388                 wpnew->who_host[0] = 0;
389 #  else
390             (void) strncpy(wpnew->who_host, utmp.ut_host, UTHOSTLEN);
391 #  endif
392 # endif /* HAVE_STRUCT_UTMP_UT_HOST */
393             wpnew->who_time = utmp.ut_time;
394 # ifdef DEAD_PROCESS
395             if (utmp.ut_type == DEAD_PROCESS)
396                 wpnew->who_status = OFFLINE;
397             else
398 # endif /* DEAD_PROCESS */
399             if (utmp.ut_name[0] == '\0')
400                 wpnew->who_status = OFFLINE;
401             else {
402                 (void) strncpy(wpnew->who_new, utmp.ut_name, UTNAMLEN);
403                 wpnew->who_status = ONLINE;
404             }
405 # ifdef WHODEBUG
406             debugwholist(wpnew, wp);
407 # endif /* WHODEBUG */
408
409             wpnew->who_next = wp;       /* link in a new 'who' */
410             wpnew->who_prev = wp->who_prev;
411             wpnew->who_prev->who_next = wpnew;
412             wp->who_prev = wpnew;       /* linked in now */
413         }
414     }
415 #ifdef HAVE_GETUTENT
416     endutent();
417 #else
418     (void) close(utmpfd);
419 #endif
420 # if defined(HAVE_STRUCT_UTMP_UT_HOST) && defined(_SEQUENT_)
421     endutent();
422 # endif
423 #endif /* !WINNT_NATIVE */
424
425     if (force || vp == NULL)
426         return;
427
428     /*
429      * The state of all logins is now known, so we can search the user's list
430      * of watchables to print the interesting ones.
431      */
432     for (alldone = 0; !alldone && *vp != NULL && **vp != '\0' &&
433          *(vp + 1) != NULL && **(vp + 1) != '\0';
434          vp += 2) {             /* args used in pairs... */
435
436         if (eq(*vp, STRany) && eq(*(vp + 1), STRany))
437             alldone = 1;
438
439         for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
440             if (wp->who_status & ANNOUNCE ||
441                 (!eq(STRany, vp[0]) &&
442                  !Gmatch(str2short(wp->who_name), vp[0]) &&
443                  !Gmatch(str2short(wp->who_new),  vp[0])) ||
444                 (!Gmatch(str2short(wp->who_tty),  vp[1]) &&
445                  !eq(STRany, vp[1])))
446                 continue;       /* entry doesn't qualify */
447             /* already printed or not right one to print */
448
449
450             if (wp->who_time == 0)/* utmp entry was cleared */
451                 wp->who_time = watch_period;
452
453             if ((wp->who_status & OFFLINE) &&
454                 (wp->who_name[0] != '\0')) {
455                 if (!firsttime)
456                     print_who(wp);
457                 wp->who_name[0] = '\0';
458                 wp->who_status |= ANNOUNCE;
459                 continue;
460             }
461             if (wp->who_status & ONLINE) {
462                 if (!firsttime)
463                     print_who(wp);
464                 (void) strcpy(wp->who_name, wp->who_new);
465                 wp->who_status |= ANNOUNCE;
466                 continue;
467             }
468             if (wp->who_status & CHANGED) {
469                 if (!firsttime)
470                     print_who(wp);
471                 (void) strcpy(wp->who_name, wp->who_new);
472                 wp->who_status |= ANNOUNCE;
473                 continue;
474             }
475         }
476     }
477 #ifdef BSDSIGS
478     (void) sigsetmask(omask);
479 #else
480     (void) sigrelse(SIGINT);
481 #endif
482 }
483
484 #ifdef WHODEBUG
485 static void
486 debugwholist(new, wp)
487     struct who *new, *wp;
488 {
489     struct who *a;
490
491     a = whohead.who_next;
492     while (a->who_next != NULL) {
493         xprintf("%s/%s -> ", a->who_name, a->who_tty);
494         a = a->who_next;
495     }
496     xprintf("TAIL\n");
497     if (a != &whotail) {
498         xprintf(CGETS(26, 3, "BUG! last element is not whotail!\n"));
499         abort();
500     }
501     a = whotail.who_prev;
502     xprintf(CGETS(26, 4, "backward: "));
503     while (a->who_prev != NULL) {
504         xprintf("%s/%s -> ", a->who_name, a->who_tty);
505         a = a->who_prev;
506     }
507     xprintf("HEAD\n");
508     if (a != &whohead) {
509         xprintf(CGETS(26, 5, "BUG! first element is not whohead!\n"));
510         abort();
511     }
512     if (new)
513         xprintf(CGETS(26, 6, "new: %s/%s\n"), new->who_name, new->who_tty);
514     if (wp)
515         xprintf("wp: %s/%s\n", wp->who_name, wp->who_tty);
516 }
517 #endif /* WHODEBUG */
518
519
520 static void
521 print_who(wp)
522     struct who *wp;
523 {
524 #ifdef HAVE_STRUCT_UTMP_UT_HOST
525     Char   *cp = str2short(CGETS(26, 7, "%n has %a %l from %m."));
526 #else
527     Char   *cp = str2short(CGETS(26, 8, "%n has %a %l."));
528 #endif /* HAVE_STRUCT_UTMP_UT_HOST */
529     struct varent *vp = adrof(STRwho);
530     Char buf[BUFSIZE];
531
532     if (vp && vp->vec && vp->vec[0])
533         cp = vp->vec[0];
534
535     tprintf(FMT_WHO, buf, cp, BUFSIZE, NULL, wp->who_time, (ptr_t) wp);
536     for (cp = buf; *cp;)
537         xputwchar(*cp++);
538     xputchar('\n');
539 } /* end print_who */
540
541
542 const char *
543 who_info(ptr, c, wbuf, wbufsiz)
544     ptr_t ptr;
545     int c;
546     char *wbuf;
547     size_t wbufsiz;
548 {
549     struct who *wp = (struct who *) ptr;
550 #ifdef HAVE_STRUCT_UTMP_UT_HOST
551     char *wb = wbuf;
552     int flg;
553     char *pb;
554 #endif /* HAVE_STRUCT_UTMP_UT_HOST */
555
556     switch (c) {
557     case 'n':           /* user name */
558         switch (wp->who_status & STMASK) {
559         case ONLINE:
560         case CHANGED:
561             return wp->who_new;
562         case OFFLINE:
563             return wp->who_name;
564         default:
565             break;
566         }
567         break;
568
569     case 'a':
570         switch (wp->who_status & STMASK) {
571         case ONLINE:
572             return CGETS(26, 9, "logged on");
573         case OFFLINE:
574             return CGETS(26, 10, "logged off");
575         case CHANGED:
576             xsnprintf(wbuf, wbufsiz, CGETS(26, 11, "replaced %s on"),
577                       wp->who_name);
578             return wbuf;
579         default:
580             break;
581         }
582         break;
583
584 #ifdef HAVE_STRUCT_UTMP_UT_HOST
585     case 'm':
586         if (wp->who_host[0] == '\0')
587             return CGETS(26, 12, "local");
588         else {
589             /* the ':' stuff is for <host>:<display>.<screen> */
590             for (pb = wp->who_host,
591                 flg = isdigit((unsigned char)*pb) ? '\0' : '.';
592                 *pb != '\0' && (*pb != flg || ((pb = strchr(pb, ':')) != 0));
593                  pb++) {
594                 if (*pb == ':')
595                     flg = '\0';
596                 *wb++ = isupper((unsigned char)*pb) ?
597                     tolower((unsigned char)*pb) : *pb;
598             }
599             *wb = '\0';
600             return wbuf;
601         }
602
603     case 'M':
604         if (wp->who_host[0] == '\0')
605             return CGETS(26, 12, "local");
606         else {
607             for (pb = wp->who_host; *pb != '\0'; pb++)
608                 *wb++ = isupper((unsigned char)*pb) ?
609                     tolower((unsigned char)*pb) : *pb;
610             *wb = '\0';
611             return wbuf;
612         }
613 #endif /* HAVE_STRUCT_UTMP_UT_HOST */
614
615     case 'l':
616         return wp->who_tty;
617
618     default:
619         wbuf[0] = '%';
620         wbuf[1] = (char) c;
621         wbuf[2] = '\0';
622         return wbuf;
623     }
624     return NULL;
625 }
626
627 void
628 /*ARGSUSED*/
629 dolog(v, c)
630 Char **v;
631 struct command *c;
632 {
633     struct who *wp;
634     struct varent *vp;
635
636     USE(v);
637     USE(c);
638     vp = adrof(STRwatch);       /* lint insists vp isn't used unless we */
639     if (vp == NULL)             /* unless we assign it outside the if */
640         stderror(ERR_NOWATCH);
641     resetwatch();
642     wp = whohead.who_next;
643     while (wp->who_next != NULL) {
644         wp->who_name[0] = '\0';
645         wp = wp->who_next;
646     }
647 }
648
649 # ifdef HAVE_STRUCT_UTMP_UT_HOST
650 size_t
651 utmphostsize()
652 {
653     return UTHOSTLEN;
654 }
655
656 char *
657 utmphost()
658 {
659     char *tty = short2str(varval(STRtty));
660     struct who *wp;
661     char *host = NULL;
662
663     watch_login(1);
664     
665     for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
666         if (strcmp(tty, wp->who_tty) == 0)
667             host = wp->who_host;
668         wp->who_name[0] = '\0';
669     }
670     resetwatch();
671     return host;
672 }
673 # endif /* HAVE_STRUCT_UTMP_UT_HOST */
674
675 #ifdef WINNT_NATIVE
676 void add_to_who_list(name, mach_nm)
677     char *name;
678     char *mach_nm;
679 {
680
681     struct who *wp, *wpnew;
682     int comp = -1;
683
684     wp = whohead.who_next;
685     while (wp->who_next && (comp = strncmp(wp->who_tty,mach_nm,UTLINLEN)) < 0)
686         wp = wp->who_next;/* find that tty! */
687
688     if (wp->who_next && comp == 0) {    /* found the tty... */
689
690         if (*name == '\0') {
691             wp->who_time = 0;
692             wp->who_status = OFFLINE;
693         }
694         else if (strncmp(name, wp->who_name, UTNAMLEN) == 0) {
695             /* someone is logged in */ 
696             wp->who_time = 0;
697             wp->who_status = 0; /* same guy */
698         }
699         else {
700             (void) strncpy(wp->who_new, name, UTNAMLEN);
701             wp->who_time = 0;
702             if (wp->who_name[0] == '\0')
703                 wp->who_status = ONLINE;
704             else
705                 wp->who_status = CHANGED;
706         }
707     }
708     else {
709         wpnew = (struct who *) xcalloc(1, sizeof *wpnew);
710         (void) strncpy(wpnew->who_tty, mach_nm, UTLINLEN);
711         wpnew->who_time = 0;
712         if (*name == '\0')
713             wpnew->who_status = OFFLINE;
714         else {
715             (void) strncpy(wpnew->who_new, name, UTNAMLEN);
716             wpnew->who_status = ONLINE;
717         }
718 #ifdef WHODEBUG
719         debugwholist(wpnew, wp);
720 #endif /* WHODEBUG */
721
722         wpnew->who_next = wp;   /* link in a new 'who' */
723         wpnew->who_prev = wp->who_prev;
724         wpnew->who_prev->who_next = wpnew;
725         wp->who_prev = wpnew;   /* linked in now */
726     }
727 }
728 #endif /* WINNT_NATIVE */
729 #endif /* HAVENOUTMP */