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