xref: /freebsd/contrib/tcsh/tc.who.c (revision 389e4940069316fe667ffa263fa7d6390d0a960f)
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 */
720