1 /*
2  * help.c: handles the help stuff for irc
3  *
4  * Written by Michael Sandrof
5  * Extensively modified by Troy Rollo
6  * Re-modified by Matthew Green
7  *
8  * Copyright(c) 1992
9  *
10  * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT
11  */
12 
13 /*
14  * This has been replaced almost entirely from the original by Michael
15  * Sandrof in order to fit in with the multiple screen code.
16  *
17  * ugh, this wasn't easy to do, but I got there, after working out what
18  * had been changed and why, by myself - phone, October 1992.
19  *
20  * And when I started getting /window create working, I discovered new
21  * bugs, and there has been a few more major changes in here again.
22  * It is invalid to call help from more than one screen, at the moment,
23  * because there is to much to keep track of - phone, jan 1993.
24  */
25 
26 #if 0
27 static char rcsid[] = "@(#)$Id: help.c 432 2013-11-07 03:00:24Z tcava $";
28 #endif
29 
30 #include "irc.h"
31 static char cvsrevision[] = "$Id: help.c 432 2013-11-07 03:00:24Z tcava $";
32 CVS_REVISION(help_c)
33 
34 #ifdef WANT_EPICHELP
35 #include "struct.h"
36 #include "help.h"
37 #include "input.h"
38 #include "ircaux.h"
39 #include "hook.h"
40 #include "output.h"
41 #include "screen.h"
42 #include "server.h"
43 #include "ircterm.h"
44 #include "vars.h"
45 #include "window.h"
46 #include <sys/stat.h>
47 #include "bsdglob.h"
48 #define MAIN_SOURCE
49 #include "modval.h"
50 
51 /* Forward declarations */
52 
53 static	void	help_me 		(char *, char *);
54 static	void	help_show_paused_topic 	(char *, char *);
55 static	void	create_help_window 	(void);
56 static	void	set_help_screen 	(Screen *);
57 static	void	help_put_it	(const char *topic, const char *format, ...);
58 
59 /*
60  * A few variables here - A lot added to get help working with
61  * non - recursive calls to irc_io, and also have it still
62  * reading things from the server(s), so not to ping timeout.
63  */
64 static	int	dont_pause_topic = 0;
65 static	int	entry_size;
66 static	int	finished_help_paging = 0;
67 static	FILE *	help_fp;
68 #define HELP_PAUSED_LINES_MAX 500
69 static	int	help_paused_lines = 0;
70 static	char *	help_paused_topic[HELP_PAUSED_LINES_MAX]; /* Should be enough */
71 static	Screen *help_screen = (Screen *) 0;
72 static	int	help_show_directory = 0;
73 static	char	help_topic_list[BIG_BUFFER_SIZE + 1];
74 static	Window *help_window = (Window *) 0;
75 static	char	no_help[] = "NOHELP";
76 static	char	paused_topic[BIG_BUFFER_SIZE + 1];
77 static	char *	this_arg;
78 static	int	use_help_window = 0;
79 
80 
81 /*
82  * show_help:  show's either a page of text from a help_fp, or the whole
83  * thing, depending on the value of HELP_PAGER_VAR.  If it gets to the end,
84  * (in either case it will eventally), it closes the file, and returns 0
85  * to indicate this.
86  */
show_help(Window * window,char * name)87 static	int	show_help (Window *window, char *name)
88 {
89 	Window	*old_target_window = target_window;
90 	int	rows = 0;
91 	char	line[256];
92 
93 	target_window = window ? window : current_window;
94 
95 	if (get_int_var(HELP_PAGER_VAR))
96 		rows = window->display_size;
97 
98 	while (rows)
99 	{
100  		if (!fgets(line, 255, help_fp))
101 		{
102 			fclose(help_fp);
103 			help_fp = NULL;
104 			target_window = old_target_window;
105 			return 0;
106 		}
107 
108 		if (*(line + strlen(line) - 1) == '\n')
109 			*(line + strlen(line) - 1) = (char) 0;
110 
111 		/*
112 		 * This is for compatability with ircII-4.4
113 		 */
114 		if (*line == '!' || *line == '#')
115 			continue;
116 
117 		help_put_it(name, "%s", line);
118 		rows--;
119 	}
120 
121 	target_window = old_target_window;
122 	return (1);
123 }
124 
125 /*
126  * help_prompt: The main procedure called to display the help file
127  * currently being accessed.  Using add_wait_prompt(), it sets it
128  * self up to be recalled when the next page is asked for.   If
129  * called when we have finished paging the help file, we exit, as
130  * there is nothing left to show.  If line is 'q' or 'Q', exit the
131  * help pager, clean up, etc..  If all is cool for now, we call
132  * show_help, and either if its finished, exit, or prompt for the
133  * next page.   From here, if we've finished the help page, and
134  * doing help prompts, prompt for the help..
135  */
help_prompt(char * name,char * line)136 static	void	help_prompt (char *name, char *line)
137 {
138 	if (finished_help_paging)
139 	{
140 		if (*paused_topic)
141 			help_show_paused_topic(paused_topic, empty_string);
142 		return;
143 	}
144 
145 	if (line && toupper(*line) == 'Q')
146 	{
147 		finished_help_paging = 1;
148 #if 0
149 		help_paused_lines = 0;		/* Thanks robo */
150 #endif
151 		fclose(help_fp);
152 		help_fp = NULL;
153 		set_help_screen((Screen *) 0);
154 		return;
155 	}
156 
157 	if (show_help(help_window, name))
158 	{
159 		if (dumb_mode)
160 			help_prompt(name, NULL);
161 		else
162 			add_wait_prompt("*** Hit any key for more, 'q' to quit ***",
163 				help_prompt, name, WAIT_PROMPT_KEY, 1);
164 	}
165 	else
166 	{
167 		finished_help_paging = 1;
168 		if (help_fp)
169 			fclose(help_fp);
170 		help_fp = NULL;
171 
172 		if (help_show_directory)
173 		{
174 			if (get_int_var(HELP_PAGER_VAR))
175 			{
176 			    if (dumb_mode)
177 				help_show_paused_topic(name, empty_string);
178 			    else
179 				add_wait_prompt("*** Hit any key to end ***",
180 					help_show_paused_topic, paused_topic,
181 					WAIT_PROMPT_KEY, 1);
182 			}
183 			else
184 			{
185 			    help_show_paused_topic(paused_topic, empty_string);
186 			    set_help_screen((Screen *) 0);
187 			}
188 			help_show_directory = 0;
189 			return;
190 		}
191 	}
192 
193 	if (finished_help_paging)
194 	{
195 		if (get_int_var(HELP_PROMPT_VAR))
196 		{
197 			char	tmp[BIG_BUFFER_SIZE + 1];
198 
199 			sprintf(tmp, "%s%sHelp? ", help_topic_list,
200 				*help_topic_list ? space : empty_string);
201 			if (!dumb_mode)
202 				add_wait_prompt(tmp, help_me, help_topic_list,
203 					WAIT_PROMPT_LINE, 1);
204 		}
205 		else
206 		{
207 			if (*paused_topic)
208 				help_show_paused_topic(paused_topic, empty_string);
209 			set_help_screen((Screen *) 0);
210 		}
211 	}
212 }
213 
214 /*
215  * help_topic:  Given a topic, we search the help directory, and try to
216  * find the right file, if all is cool, and we can open it, or zcat it,
217  * then we call help_prompt to get the actually displaying of the file
218  * on the road.
219  */
help_topic(char * path,char * name)220 static	void	help_topic (char *path, char *name)
221 {
222 	char	*filename = NULL;
223 
224 	if (!name)
225 		return;
226 
227 	/* what is the base name? */
228 	filename = m_sprintf("%s/%s", path, name);
229 	if (filename[strlen(filename)-1] == '/')
230 		chop(filename, 1);
231 
232 	/* let uzfopen have all the fun */
233 	if ((help_fp = uzfopen (&filename, path, 0)))
234 	{
235 		/* Isnt this a heck of a lot better then the kludge you were using? */
236 		help_put_it(name, "*** Help on %s", name);
237 		help_prompt(name, NULL);
238 	}
239 	else
240 		help_put_it (name, "*** No help available on %s: Use ? for list of topics", name);
241 
242 	new_free(&filename);
243 	return;
244 }
245 
246 /*
247  * help_pause_add_line: this procedure does a help_put_it() call, but
248  * puts off the calling, until help_show_paused_topic() is called.
249  * I do this because I need to create the list of help topics, but
250  * not show them, until we've seen the whole file, so we called
251  * help_show_paused_topic() when we've seen the file, if it is needed.
252  */
help_pause_add_line(char * format,...)253 static 	void 	help_pause_add_line (char *format, ...)
254 {
255 	char	buf[BIG_BUFFER_SIZE];
256 	va_list args;
257 
258 	va_start (args, format);
259 	vsnprintf(buf, BIG_BUFFER_SIZE - 1, format, args);
260 	va_end (args);
261 	if ((help_paused_lines + 1) >= HELP_PAUSED_LINES_MAX)
262 		ircpanic("help_pause_add_line: would overflow the buffer");
263 	malloc_strcpy(&help_paused_topic[help_paused_lines++], buf);
264 }
265 
266 /*
267  * help_show_paused_topic:  see above.  Called when we've seen the
268  * whole help file, and we have a list of topics to display.
269  */
help_show_paused_topic(char * name,char * line)270 static	void	help_show_paused_topic (char *name, char *line)
271 {
272 	static int i = 0;
273 	int j = 0;
274 	int rows;
275 
276 	if (!help_paused_lines)
277 		return;
278 
279 	if (toupper(*line) == 'Q')
280 		i = help_paused_lines + 1;	/* just big enough */
281 
282 	rows = help_window->display_size;
283 	if (i < help_paused_lines)
284 	{
285 		for (j = 0; j < rows; j++)
286 		{
287 			help_put_it (name, "%s", help_paused_topic[i]);
288 			new_free(&help_paused_topic[i]);
289 
290 			/* if we're done, the recurse to break loop */
291 			if (++i >= help_paused_lines)
292 				break;
293 		}
294 		if (!dumb_mode)
295 		{
296 			if ((i < help_paused_lines) && get_int_var(HELP_PAGER_VAR))
297 				add_wait_prompt("[MORE]", help_show_paused_topic, name, WAIT_PROMPT_KEY, 1);
298 		}
299 		else
300 			help_show_paused_topic(name, line);
301 	}
302 
303 	/*
304 	 * This cant be an else of the previous if because 'i' can
305 	 * change in the previous if and we need to test it again
306 	 */
307 	if (i >= help_paused_lines)
308 	{
309 		if (get_int_var(HELP_PROMPT_VAR))
310 		{
311 			char	buf[BIG_BUFFER_SIZE];
312 
313 			sprintf(buf, "%s%sHelp? ", name, (name && *name) ? space : empty_string);
314 			if (!dumb_mode)
315 				add_wait_prompt(buf, help_me, name, WAIT_PROMPT_LINE, 1);
316 		}
317 		else
318 			set_help_screen((Screen *) 0);
319 
320 		dont_pause_topic = 0;
321 		help_paused_lines = 0;	/* Probably should reset this ;-) */
322 		i = 0;
323 	}
324 }
325 
326 /*
327  * help_me:  The big one.  The help procedure that handles working out
328  * what was actually requested, sets up the paused topic list if it is
329  * needed, does pretty much all the hard work.
330  */
help_me(char * topics,char * args)331 static	void	help_me (char *topics, char *args)
332 {
333 	char *	ptr;
334 	glob_t	g;
335 	int	entries = 0,
336 		cnt,
337 		i,
338 		cols;
339 	struct	stat	stat_buf;
340 	char	path[BIG_BUFFER_SIZE+1];
341 	int	help_paused_first_call = 0;
342 	char *	help_paused_path = (char *) 0;
343 	char *	help_paused_name = (char *) 0;
344 	char *	temp;
345 	char	tmp[BIG_BUFFER_SIZE+1];
346 	char	buffer[BIG_BUFFER_SIZE+1];
347 	char *	pattern = NULL;
348 
349 	strcpy(help_topic_list, topics);
350 	ptr = get_string_var(HELP_PATH_VAR);
351 
352 	sprintf(path, "%s/%s", ptr, topics);
353 	for (ptr = path; (ptr = strchr(ptr, ' '));)
354 		*ptr = '/';
355 
356 	/*
357 	 * first we check access to the help dir, whinge if we can't, then
358 	 * work out we need to ask them for more help, else we check the
359 	 * args list, and do the stuff
360 	 */
361 	if (help_show_directory)
362 	{
363 		help_show_paused_topic(paused_topic, empty_string);
364 		help_show_directory = 0;
365 	}
366 
367 	finished_help_paging = 0;
368 	if (access(path, R_OK|X_OK))
369 	{
370 		help_put_it(no_help, "*** Cannot access help directory!");
371 		set_help_screen((Screen *) 0);
372 		return;
373 	}
374 
375 	this_arg = next_arg(args, &args);
376 	if (!this_arg && *help_topic_list && get_int_var(HELP_PROMPT_VAR))
377 	{
378 		if ((temp = strrchr(help_topic_list, ' ')) != NULL)
379 			*temp = '\0';
380 		else
381 			*help_topic_list = '\0';
382 
383 		sprintf(tmp, "%s%sHelp? ", help_topic_list, *help_topic_list ? space : empty_string);
384 
385 		if (!dumb_mode)
386 			add_wait_prompt(tmp, help_me, help_topic_list, WAIT_PROMPT_LINE, 1);
387 		return;
388 	}
389 
390 	if (!this_arg)
391 	{
392 		set_help_screen((Screen *) 0);
393 		return;
394 	}
395 
396 	create_help_window();
397 
398 	/*
399 	 * This is just a bogus while loop which is intended to allow
400 	 * the user to do '/help alias expressions' without having to
401 	 * include a slash inbetween the topic and subtopic.
402 	 *
403 	 * If all goes well, we 'break' at the bottom of the loop.
404 	 */
405 	while (this_arg)
406 	{
407 		entries = 0;
408 		reset_display_target();
409 
410 		if (!*this_arg)
411 			help_topic(path, NULL);
412 
413 		if (strcmp(this_arg, "?") == 0)
414 		{
415 			this_arg = empty_string;
416 			if (!dont_pause_topic)
417 				dont_pause_topic = 1;
418 		}
419 
420 		/*
421 		 * entry_size is set to the width of the longest help topic
422 		 * (adjusted for compression extensions, of course.)
423 		 */
424 		entry_size = 0;
425 
426 		/*
427 		 * Gather up the names of the files in the help directory.
428 		 */
429 		{
430 #ifndef HAVE_FCHDIR
431 			char 	opath[MAXPATHLEN + 1];
432 			getcwd(opath, MAXPATHLEN);
433 #else
434 			int 	cwd = open(".", O_RDONLY);
435 #endif
436 
437 			chdir(path);
438 			pattern = alloca(strlen(path) + 2 +
439 					 strlen(this_arg) + 3);
440 			strcpy(pattern, this_arg);
441 			strcat(pattern, "*");
442 #ifdef GLOB_INSENSITIVE
443 			bsd_glob(pattern, GLOB_INSENSITIVE /* GLOB_MARK */, NULL, &g);
444 #else
445 			bsd_glob(pattern, 0 /* GLOB_MARK */, NULL, &g);
446 #endif
447 #ifndef HAVE_FCHDIR
448 			chdir(opath);
449 #else
450 			fchdir(cwd);
451 			close(cwd);
452 #endif
453 		}
454 
455 		for (i = 0; i < g.gl_matchc; i++)
456 		{
457 			char	*tmp = g.gl_pathv[i];
458 			int 	len = strlen(tmp);
459 
460 			if (!end_strcmp(tmp, ".gz", 3))
461 				len -= 3;
462 			else if (!end_strcmp(tmp, ".bz2", 4))
463 				len -= 4;
464 			entry_size = (len > entry_size) ? len : entry_size;
465 		}
466 
467 		/*
468 		 * Right here we need to check for an 'exact match'.
469 		 * An 'exact match' would be sitting in gl_pathv[0],
470 		 * and it is 'exact' if it is identical to what we are
471 		 * looking for, or if it is the same except that it has
472 		 * a compression extension on it
473 		 */
474 		if (g.gl_matchc > 1)
475 		{
476 			char *str1 = g.gl_pathv[0];
477 			char *str2 = this_arg;
478 			int len1 = strlen(str1);
479 			int len2 = strlen(str2);
480 
481 
482 			     if (len1 == len2 && !my_stricmp(str1, str2))
483 				entries = 1;
484 			else if (len1 - 3 == len2 && !my_strnicmp(str1, str2, len2) && !end_strcmp(str1, ".gz", 3))
485 				entries = 1;
486 			else if (len1 - 2 == len2 && !my_strnicmp(str1, str2, len2) && !end_strcmp(str1, ".Z", 2))
487 				entries = 1;
488 			else if (len1 - 2 == len2 && !my_strnicmp(str1, str2, len2) && !end_strcmp(str1, ".z", 2))
489 				entries = 1;
490 		}
491 
492 		if (!*help_topic_list)
493 			dont_pause_topic = 1;
494 
495 /* reformatted */
496 /*
497  * entries: -1 means something really died, 0 means there
498  * was no help, 1, means it wasn't a directory, and so to
499  * show the help file, and the default means to add the
500  * stuff to the paused topic list..
501  */
502 if (!entries)
503 	entries = g.gl_matchc;
504 
505 switch (entries)
506 {
507 	case -1:
508 	{
509 		help_put_it(no_help, "*** Error during help function: %s", strerror(errno));
510 		set_help_screen(NULL);
511 		if (help_paused_first_call)
512 		{
513 			help_topic(help_paused_path, help_paused_name);
514 			help_paused_first_call = 0;
515 			new_free(&help_paused_path);
516 			new_free(&help_paused_name);
517 		}
518 		return;
519 	}
520 	case 0:
521 	{
522 		help_put_it(this_arg, "*** No help available on %s: Use ? for list of topics", this_arg);
523 		if (!get_int_var(HELP_PROMPT_VAR))
524 		{
525 			set_help_screen(NULL);
526 			break;
527 		}
528 		sprintf(tmp, "%s%sHelp? ", help_topic_list, *help_topic_list ? space : empty_string);
529 		if (!dumb_mode)
530 			add_wait_prompt(tmp, help_me, help_topic_list, WAIT_PROMPT_LINE, 1);
531 
532 		if (help_paused_first_call)
533 		{
534 			help_topic(help_paused_path, help_paused_name);
535 			help_paused_first_call = 0;
536 			new_free(&help_paused_path);
537 			new_free(&help_paused_name);
538 		}
539 
540 		break;
541 	}
542 	case 1:
543 	{
544 		sprintf(tmp, "%s/%s", path, g.gl_pathv[0]);
545 		stat(tmp, &stat_buf);
546 		if (stat_buf.st_mode & S_IFDIR)
547 		{
548 			strcpy(path, tmp);
549 			if (*help_topic_list)
550 				strcat(help_topic_list, space);
551 
552 			strcat(help_topic_list, g.gl_pathv[0]);
553 
554 			if (!(this_arg = next_arg(args, &args)))
555 			{
556 				help_paused_first_call = 1;
557 				malloc_strcpy(&help_paused_path, path);
558 				malloc_strcpy(&help_paused_name, g.gl_pathv[0]);
559 				dont_pause_topic = -1;
560 				this_arg = "?";
561 			}
562 			bsd_globfree(&g);
563 			continue;
564 		}
565 		else
566 		{
567 			help_topic(path, g.gl_pathv[0]);
568 			finished_help_paging = 0;
569 			break;
570 		}
571 	}
572 	default:
573 	{
574 		help_show_directory = 1;
575 		strcpy(paused_topic, help_topic_list);
576 		help_pause_add_line("*** %s choices:", help_topic_list);
577 		entry_size += 2;
578 		cols = (current_term->TI_cols - 10) / entry_size;
579 
580 		strcpy(buffer, empty_string);
581 		cnt = 0;
582 
583 		for (i = 0; i < entries; i++)
584 		{
585 			if (!end_strcmp(g.gl_pathv[i], ".gz", 3))
586 				chop(g.gl_pathv[i], 3);
587 			else if (!end_strcmp(g.gl_pathv[i], ".bz2", 4))
588 				chop(g.gl_pathv[i], 4);
589 			strcat(buffer, g.gl_pathv[i]);
590 
591 			/*
592 			 * Since we already know how many columns each
593 			 * line will contain, we check to see if we have
594 			 * accumulated that many entries.  If we have, we
595 			 * output the line to the screen.
596 			 */
597 			if (++cnt == cols)
598 			{
599 				help_pause_add_line("%s", buffer);
600 				strcpy(buffer, empty_string);
601 				cnt = 0;
602 			}
603 
604 			/*
605 			 * If we have not finished this line, then we have
606 			 * to pad the name length out to the expected width.
607 			 * 'entry_size' is the column width.  We also have
608 			 * do adjust for compression extension.
609 			 */
610 			else
611 				strextend(buffer, ' ', entry_size - strlen(g.gl_pathv[i]));
612 		}
613 
614 		help_pause_add_line("%s", buffer);
615 		if (help_paused_first_call)
616 		{
617 			help_topic(help_paused_path, help_paused_name);
618 			help_paused_first_call = 0;
619 			new_free(&help_paused_path);
620 			new_free(&help_paused_name);
621 		}
622 		if (dont_pause_topic == 1)
623 		{
624 			help_show_paused_topic(paused_topic, empty_string);
625 			help_show_directory = 0;
626 		}
627 		break;
628 	}
629 }
630 /* end of reformatting */
631 
632 
633 		bsd_globfree(&g);
634 		break;
635 	}
636 
637 	/*
638 	 * This one is for when there was never a topic and the prompt
639 	 * never got a topic..  and help_screen was never reset..
640 	 * phone, jan 1993.
641 	 */
642 	if (!*help_topic_list && finished_help_paging)
643 		set_help_screen((Screen *) 0);
644 }
645 
646 /*
647  * help: the HELP command, gives help listings for any and all topics out
648  * there
649  */
BUILT_IN_COMMAND(epichelp)650 BUILT_IN_COMMAND(epichelp)
651 {
652 	char	*help_path;
653 
654 	finished_help_paging = 0;
655 	help_show_directory = 0;
656 	dont_pause_topic = 0;
657 	use_help_window = 0;
658 
659 	/*
660 	 * The idea here is to work out what sort of help we are using -
661 	 * either the installed help files, or some help service, what
662 	 * ever it maybe.  Once we have worked this out, if we are using
663 	 * a help window, set it up properly.
664 	 */
665 	help_path = get_string_var(HELP_PATH_VAR);
666 
667 	if (!help_path || !*help_path || access(help_path, R_OK | X_OK))
668 	{
669 		help_put_it(no_help, "*** HELP_PATH variable not set or set to an invalid path");
670 		return;
671 	}
672 
673 	/* Allow us to wait until help is finished */
674 	if (!my_strnicmp(args, "-wait", 2))
675 	{
676 		while (help_screen)
677 			io("help");
678 		return;
679 	}
680 
681 	if (help_path && help_screen && help_screen != current_window->screen)
682 	{
683 		say("You may not run help in two screens");
684 		return;
685 	}
686 
687 	help_screen = current_window->screen;
688 	help_window = (Window *) 0;
689 	help_me(empty_string, (args && *args) ? args : "?");
690 }
691 
692 
693 
694 
create_help_window(void)695 static	void create_help_window (void)
696 {
697 	if (help_window)
698 		return;
699 
700 	if (!dumb_mode && get_int_var(HELP_WINDOW_VAR))
701 	{
702 		use_help_window = 1;
703 		help_window = new_window(current_window->screen);
704 		help_window->hold_mode = OFF;
705 		help_window->window_level = LOG_HELP;
706 		update_all_windows();
707 	}
708 	else
709 		help_window = current_window;
710 }
711 
712 
713 
set_help_screen(Screen * screen)714 static	void	set_help_screen (Screen *screen)
715 {
716 	help_screen = screen;
717 	if (!help_screen && help_window)
718 	{
719 		if (use_help_window)
720 		{
721 			int display = window_display;
722 
723 			window_display = 0;
724 			delete_window(help_window);
725 			window_display = display;
726 		}
727 		help_window = (Window *) 0;
728 		update_all_windows();
729 	}
730 }
731 
help_put_it(const char * topic,const char * format,...)732 static	void	help_put_it	(const char *topic, const char *format, ...)
733 {
734 	char putbuf[BIG_BUFFER_SIZE * 3 + 1];
735 
736 	if (format)
737 	{
738 		va_list args;
739 		va_start (args, format);
740 		vsnprintf(putbuf, BIG_BUFFER_SIZE * 3, format, args);
741 		va_end(args);
742 
743 		if (do_hook(HELP_LIST, "%s %s", topic, putbuf))
744 		{
745 			int old_level = who_level;
746 			Window *old_target_window = target_window;
747 
748 			/*
749 			 * LOG_HELP is a completely bogus mode.  We use
750 			 * it only to make sure that the current level is
751 			 * not LOG_CURRENT, so that the to_window will stick.
752 			 */
753 			who_level = LOG_HELP;
754 			if (help_window)
755 				target_window = help_window;
756 			add_to_screen(putbuf);
757 			target_window = old_target_window;
758 			who_level = old_level;
759 		}
760 	}
761 }
762 #endif
763 
764