Use BBLOCK.
[dragonfly.git] / contrib / tcsh-6 / tc.who.c
blob2aa0686b7f4fe05e4a40bf5ba013e77588804dfa
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.
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.
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.
33 #include "sh.h"
35 RCSID("$tcsh: tc.who.c,v 3.51 2006/03/03 22:08:45 amold Exp $")
37 #include "tc.h"
39 #ifndef HAVENOUTMP
41 * kfk 26 Jan 1984 - for login watch functions.
43 #include <ctype.h>
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
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 */
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;
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 */
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 */
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;
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 (struct who *, struct who *);
139 #endif
140 static void print_who (struct who *);
143 #define ONLINE 01
144 #define OFFLINE 02
145 #define CHANGED 04
146 #define STMASK 07
147 #define ANNOUNCE 010
148 #define CLEARED 020
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.
156 void
157 initwatch(void)
159 whohead.who_next = &whotail;
160 whotail.who_prev = &whohead;
161 stlast = 1;
162 #ifdef WHODEBUG
163 debugwholist(NULL, NULL);
164 #endif /* WHODEBUG */
167 void
168 resetwatch(void)
170 watch_period = 0;
171 stlast = 0;
175 * Karl Kleinpaste, 26 Jan 1984.
176 * Watch /etc/utmp for login/logout changes.
178 void
179 watch_login(int force)
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 */
205 /* stop SIGINT, lest our login list get trashed. */
206 pintr_disabled++;
207 cleanup_push(&pintr_disabled, disabled_cleanup);
209 v = adrof(STRwatch);
210 if ((v == NULL || v->vec == NULL) && !force) {
211 cleanup_until(&pintr_disabled);
212 return; /* no names to watch */
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;
219 else
220 interval = 0;
222 (void) time(&t);
223 #ifdef WINNT_NATIVE
225 * Since NCB_ASTATs take time, start em async at least 90 secs
226 * before we are due -amol 6/5/97
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;
235 #endif /* WINNT_NATIVE */
236 if (t - watch_period < interval) {
237 cleanup_until(&pintr_disabled);
238 return; /* not long enough yet... */
240 watch_period = t;
241 #ifdef WINNT_NATIVE
242 ncbs_posted = 0;
243 #else /* !WINNT_NATIVE */
246 * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de>
247 * Don't open utmp all the time, stat it first...
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;
257 if (stlast == sta.st_mtime) {
258 cleanup_until(&pintr_disabled);
259 return;
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;
273 cleanup_push(&utmpfd, open_cleanup);
274 #endif
277 * xterm clears the entire utmp entry - mark everyone on the status list
278 * OFFLINE or we won't notice X "logouts"
280 for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next)
281 wp->who_status = OFFLINE | CLEARED;
284 * Read in the utmp file, sort the entries, and update existing entries or
285 * add new entries to the status list.
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
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 */
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! */
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;
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;
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 */
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;
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;
380 # ifdef WHODEBUG
381 debugwholist(wpnew, wp);
382 # endif /* WHODEBUG */
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 */
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 */
400 if (force || vp == NULL) {
401 cleanup_until(&pintr_disabled);
402 return;
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.
409 for (alldone = 0; !alldone && *vp != NULL && **vp != '\0' &&
410 *(vp + 1) != NULL && **(vp + 1) != '\0';
411 vp += 2) { /* args used in pairs... */
413 if (eq(*vp, STRany) && eq(*(vp + 1), STRany))
414 alldone = 1;
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 */
427 if (wp->who_status & CLEARED) {/* utmp entry was cleared */
428 wp->who_time = watch_period;
429 wp->who_status &= ~CLEARED;
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;
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;
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;
456 cleanup_until(&pintr_disabled);
459 #ifdef WHODEBUG
460 static void
461 debugwholist(struct who *new, struct who *wp)
463 struct who *a;
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;
470 xprintf("TAIL\n");
471 if (a != &whotail) {
472 xprintf(CGETS(26, 3, "BUG! last element is not whotail!\n"));
473 abort();
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;
481 xprintf("HEAD\n");
482 if (a != &whohead) {
483 xprintf(CGETS(26, 5, "BUG! first element is not whohead!\n"));
484 abort();
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);
491 #endif /* WHODEBUG */
494 static void
495 print_who(struct who *wp)
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;
505 if (vp && vp->vec && vp->vec[0])
506 cp = vp->vec[0];
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 */
517 char *
518 who_info(ptr_t ptr, int c)
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 */
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;
539 break;
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;
552 break;
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;
571 *wb = '\0';
572 return wbuf;
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;
588 #endif /* HAVE_STRUCT_UTMP_UT_HOST */
590 case 'l':
591 return strsave(wp->who_tty);
593 default:
594 wbuf = xmalloc(3);
595 wbuf[0] = '%';
596 wbuf[1] = (char) c;
597 wbuf[2] = '\0';
598 return wbuf;
600 return NULL;
603 void
604 /*ARGSUSED*/
605 dolog(Char **v, struct command *c)
607 struct who *wp;
608 struct varent *vp;
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;
623 # ifdef HAVE_STRUCT_UTMP_UT_HOST
624 size_t
625 utmphostsize(void)
627 return UTHOSTLEN;
630 char *
631 utmphost(void)
633 char *tty = short2str(varval(STRtty));
634 struct who *wp;
635 char *host = NULL;
637 watch_login(1);
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';
644 resetwatch();
645 return host;
647 # endif /* HAVE_STRUCT_UTMP_UT_HOST */
649 #ifdef WINNT_NATIVE
650 void add_to_who_list(name, mach_nm)
651 char *name;
652 char *mach_nm;
655 struct who *wp, *wpnew;
656 int comp = -1;
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! */
662 if (wp->who_next && comp == 0) { /* found the tty... */
664 if (*name == '\0') {
665 wp->who_time = 0;
666 wp->who_status = OFFLINE;
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 */
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;
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;
692 #ifdef WHODEBUG
693 debugwholist(wpnew, wp);
694 #endif /* WHODEBUG */
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 */
702 #endif /* WINNT_NATIVE */
703 #endif /* HAVENOUTMP */