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