1 /*
2  * help.c: handles the help stuff for irc
3  *
4  * Written by Michael Sandrof, Troy Rollo and Matthew R. Green.
5  *
6  * Copyright (c) 1990 Michael Sandrof.
7  * Copyright (c) 1991, 1992 Troy Rollo.
8  * Copyright (c) 1992-2017 Matthew R. Green.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include "irc.h"
36 IRCII_RCSID("@(#)$eterna: help.c,v 1.108 2017/07/09 06:58:07 mrg Exp $");
37 
38 #include <dirent.h>
39 #define NLENGTH(d) (my_strlen((d)->d_name)
40 
41 #include <sys/stat.h>
42 
43 #include "ircterm.h"
44 #include "server.h"
45 #include "vars.h"
46 #include "ircaux.h"
47 #include "input.h"
48 #include "window.h"
49 #include "screen.h"
50 #include "output.h"
51 #include "help.h"
52 #include "scandir.h"
53 #include "sl_irc.h"
54 
55 #ifdef ALPHASORT_VOID_ARG
56 typedef const void *compar_type;
57 #else
58 typedef const struct dirent **compar_type;
59 #endif
60 
61 static	void	help_me(u_char *, u_char *);
62 static	void	help_show_paused_topic(u_char *, u_char *);
63 static	void	create_help_window(void);
64 static	void	set_help_screen(Screen *);
65 static	int	compar(compar_type, compar_type);
66 static	int	selectent(const struct dirent *);
67 static	int	show_help(Window *, u_char *);
68 static	void	help_prompt_format_add(void);
69 static	void	help_no_help(u_char *);
70 static	void	help_prompt(u_char *, u_char *);
71 static	void	help_topic(u_char *, u_char *);
72 
73 static	Window	*help_window = NULL;
74 static	FILE	*help_fp;
75 static	u_char	no_help[] = "NOHELP";
76 static	int	entry_size;
77 static	u_char	*this_arg;
78 static	int	finished_help_paging = 0;
79 static	int	help_show_directory = 0;
80 static	int	dont_pause_topic = 0;
81 static	Screen  *help_screen = NULL;
82 static	u_char	paused_topic[128];
83 static	u_char	*help_topic_list = NULL;
84 static	int	use_help_window = 0;
85 static	StringList *help_paused_lines;
86 
87 /* we are piglet */
88 
89 /* compar: used by scandir to alphabetize the help entries */
90 static	int
compar(compar_type p1,compar_type p2)91 compar(compar_type p1, compar_type p2)
92 {
93 	const struct dirent *e1 = *(const struct dirent **)p1;
94 	const struct dirent *e2 = *(const struct dirent **)p2;
95 
96 	return (my_stricmp((u_char *)e1->d_name, (u_char *)e2->d_name));
97 }
98 
99 /*
100  * selectent: used by scandir to decide which entries to include in the help
101  * listing.
102  */
103 static	int
selectent(const struct dirent * entry)104 selectent(const struct dirent *entry)
105 {
106 	if (*(entry->d_name) == '.')
107 		return (0);
108 	if (my_strnicmp((u_char *)entry->d_name, this_arg, my_strlen(this_arg)))
109 		return (0);
110 	else
111 	{
112 		int len = my_strlen(entry->d_name);
113 #ifdef ZCAT
114 		const char *temp;
115 
116 		/*
117 		 * Handle major length of filename is case of suffix .Z:
118 		 * stripping suffix length
119 		 */
120 		temp = &(entry->d_name[len - my_strlen(ZSUFFIX)]);
121 		if (!my_strcmp(temp, ZSUFFIX))
122 			len -= my_strlen(ZSUFFIX);
123 #endif /* ZCAT */
124 		entry_size = (len > entry_size) ? len : entry_size;
125 		return (1);
126 	}
127 }
128 
129 /*
130  * show_help:  show's either a page of text from a help_fp, or the whole
131  * thing, depending on the value of HELP_PAGER_VAR.  If it gets to the end,
132  * (in either case it will eventally), it closes the file, and returns 0
133  * to indicate this.
134  */
135 static	int
show_help(Window * window,u_char * name)136 show_help(Window *window, u_char *name)
137 {
138 	int	rows = 0;
139 	u_char	line[81];
140 
141 	if (!help_fp)
142 		return (0);
143 	if (window)
144 	{
145 		set_curr_scr_win(window);
146 	}
147 	else
148 	{
149 		window = curr_scr_win;
150 	}
151 	if (get_int_var(HELP_PAGER_VAR))
152 		rows = window_get_display_size(window);
153 	while (--rows)
154 	{
155 		if (fgets(CP(line), 80, help_fp))
156 		{
157 			u_char	*p = line + my_strlen(line) - 1;
158 
159 			if (*p == '\n')
160 				*p = '\0';
161 
162 			if (*line != '!' && *line != '#')
163 				help_put_it(name, "%s", line);
164 			else
165 				rows++;
166 		}
167 		else
168 			return (0);
169 	}
170 	return (1);
171 }
172 
173 /*
174  * help_prompt_format_add: format the help prompt and add it to the
175  * wait list.
176  */
177 static void
help_prompt_format_add(void)178 help_prompt_format_add(void)
179 {
180 	u_char	*tmp = NULL,
181 		*list;
182 
183 	if (term_basic())
184 		return;
185 
186 	if (help_topic_list)
187 		list = help_topic_list;
188 	else
189 		list = empty_string();
190 	malloc_snprintf(&tmp, "%s%sHelp? ", list, *list ? " " : "");
191 	add_wait_prompt(tmp, help_me, list, WAIT_PROMPT_LINE);
192 	new_free(&tmp);
193 }
194 
195 static void
help_no_help(u_char * name)196 help_no_help(u_char *name)
197 {
198 	help_put_it(name, "*** No help available on %s: Use ? "
199 			  "for list of topics", name);
200 }
201 
202 /*
203  * help_prompt: The main procedure called to display the help file
204  * currently being accessed.  Using add_wait_prompt(), it sets it
205  * self up to be recalled when the next page is asked for.   If
206  * called when we have finished paging the help file, we exit, as
207  * there is nothing left to show.  If line is 'q' or 'Q', exit the
208  * help pager, clean up, etc..  If all is cool for now, we call
209  * show_help, and either if its finished, exit, or prompt for the
210  * next page.   From here, if we've finished the help page, and
211  * doing help prompts, prompt for the help..
212  */
213 
214 static	void
help_prompt(u_char * name,u_char * line)215 help_prompt(u_char *name, u_char *line)
216 {
217 	if (finished_help_paging)
218 	{
219 		if (*paused_topic)
220 			help_show_paused_topic(paused_topic, NULL);
221 		return;
222 	}
223 
224 	if (line && ((*line == 'q') || (*line == 'Q')))
225 	{
226 		finished_help_paging = 1;
227 		if (help_fp)
228 		{
229 			fclose(help_fp);
230 			help_fp = NULL;
231 		}
232 		set_help_screen(NULL);
233 		return;
234 	}
235 
236 	if (show_help(help_window, name))
237 		if (term_basic())
238 			help_prompt(name, NULL);
239 		else
240 			add_wait_prompt(UP("*** Hit any key for more, 'q' to quit ***"),
241 				help_prompt, name, WAIT_PROMPT_KEY);
242 	else
243 	{
244 		finished_help_paging = 1;
245 		if (help_fp)
246 		{
247 			fclose(help_fp);
248 			help_fp = NULL;
249 		}
250 		if (help_show_directory)
251 		{
252 			if (get_int_var(HELP_PAGER_VAR))
253 				if (term_basic())
254 					help_show_paused_topic(paused_topic, NULL);
255 				else
256 					add_wait_prompt(UP("*** Hit any key to end ***"),
257 						help_show_paused_topic, paused_topic,
258 						WAIT_PROMPT_KEY);
259 			else
260 			{
261 				help_show_paused_topic(paused_topic, NULL);
262 				set_help_screen(NULL);
263 			}
264 			help_show_directory = 0;
265 			return;
266 		}
267 	}
268 
269 	if (finished_help_paging)
270 	{
271 		if (get_int_var(HELP_PROMPT_VAR))
272 			help_prompt_format_add();
273 		else
274 		{
275 			if (*paused_topic)
276 				help_show_paused_topic(paused_topic, NULL);
277 			set_help_screen(NULL);
278 		}
279 	}
280 }
281 
282 /*
283  * help_topic:  Given a topic, we search the help directory, and try to
284  * find the right file, if all is cool, and we can open it, or zcat it,
285  * then we call help_prompt to get the actually displaying of the file
286  * on the road.
287  */
288 static	void
help_topic(u_char * path,u_char * name)289 help_topic(u_char *path, u_char *name)
290 {
291 	struct	stat sb;
292 	u_char	*filename = NULL;
293 #ifdef ZCAT
294 	int	do_zcat = 0;
295 #endif /* ZCAT */
296 
297 	if (name == NULL)
298 		return;
299 
300 	Debug(DB_HELP, "path %s, name %s", path, name);
301 	/*
302 	 * Check the existence of <name> or <name>.Z .. Handle suffix
303 	 * .Z if present.  Open the file if it isn't present, zcat the
304 	 * file if it is present, and ends with .Z ..
305 	 */
306 
307 	malloc_snprintf(&filename, "%s/%s", path, name);
308 
309 	if (stat(CP(filename), &sb) == -1)
310 	{
311 #ifdef ZCAT
312 		if (my_strcmp(name + (my_strlen(name) - my_strlen(ZSUFFIX)), ZSUFFIX))
313 		{
314 			malloc_snprintf(&filename, "%s/%s%s", path, name, ZSUFFIX);
315 			if (stat(CP(filename), &sb) == -1)
316 			{
317 				new_free(&filename);
318 				goto no_help_found;
319 			}
320 		}
321 		else
322 #endif /* ZCAT */
323 			goto no_help_found;
324 	}
325 
326 	if (my_strcmp(filename + (my_strlen(filename) - my_strlen(ZSUFFIX)), ZSUFFIX) == 0)
327 		do_zcat = 1;
328 
329 	if (!filename)
330 		goto no_help_found;
331 
332 	if (sb.st_mode & S_IFDIR)
333 		return;
334 
335 	if (help_fp)
336 		fclose(help_fp);
337 #ifdef ZCAT
338 	if (!do_zcat)
339 	{
340 #endif /* ZCAT */
341 
342 		if ((help_fp = fopen(CP(filename), "r")) == NULL)
343 			goto no_help_found;
344 #ifdef ZCAT
345 	}
346 	else
347 	{
348 		if ((help_fp = zcat(filename)) == NULL)
349 			goto no_help_found;
350 	}
351 #endif /* ZCAT */
352 
353 	/*
354 	 * Hopefully now we have got a file descriptor <help_fp>, a name
355 	 * so we start displaying the help file, calling help_prompt for
356 	 * the first time.
357 	 */
358 
359 	new_free(&filename);
360 	help_put_it(name, "*** Help on %s", name);
361 	help_prompt(name, NULL);
362 
363 	return;
364 
365 no_help_found:
366 	new_free(&filename);
367 	help_no_help(name);
368 }
369 
370 /*
371  * help_pause_add_line: this procedure does a help_put_it() call, but
372  * puts off the calling, until help_show_paused_topic() is called.
373  * I do this because I need to create the list of help topics, but
374  * not show them, until we've seen the whole file, so we called
375  * help_show_paused_topic() when we've seen the file, if it is needed.
376  */
377 
378 static	void
help_pause_add_line(char * format,...)379 help_pause_add_line(char *format, ...)
380 {
381 	va_list vl;
382 
383 	u_char	*copy = NULL;
384 
385 	va_start(vl, format);
386 	malloc_vsnprintf(&copy, format, vl);
387 	va_end(vl);
388 	if (!help_paused_lines)
389 		help_paused_lines = sl_init();
390 	sl_add(help_paused_lines, CP(copy));
391 }
392 
393 /*
394  * help_show_paused_topic:  see above.  Called when we've seen the
395  * whole help file, and we have a list of topics to display.
396  */
397 static	void
help_show_paused_topic(u_char * name,u_char * unused)398 help_show_paused_topic(u_char *name, u_char *unused)
399 {
400 	size_t	iter;
401 	u_char	*line;
402 
403 	if (!help_paused_lines)
404 		return;
405 	for (iter = 0; (line = sl_iter_fwd(help_paused_lines, &iter)) != NULL;)
406 		help_put_it(name, "%s", line);
407 	if (get_int_var(HELP_PROMPT_VAR))
408 	{
409 		u_char	*buf = NULL;
410 
411 		malloc_snprintf(&buf, "%s%sHelp? ", name, (name && *name) ? " " : "");
412 		if (!term_basic())
413 			add_wait_prompt(buf, help_me, name, WAIT_PROMPT_LINE);
414 		new_free(&buf);
415 	}
416 	else
417 		set_help_screen(NULL);
418 
419 	dont_pause_topic = 0;
420 	sl_free(help_paused_lines, 1);
421 	help_paused_lines = NULL;
422 }
423 
424 /*
425  * help_me:  The big one.  The help procedure that handles working out
426  * what was actually requested, sets up the paused topic list if it is
427  * needed, does pretty much all the hard work.
428  */
429 static	void
help_me(u_char * topics,u_char * args)430 help_me(u_char *topics, u_char *args)
431 {
432 	u_char	*ptr;
433 	struct	dirent	**namelist = NULL;
434 	int	entries,
435 		free_cnt = 0,
436 		cnt,
437 		i,
438 		cols;
439 	struct	stat sb;
440 	u_char	path[PATH_MAX+1];
441 	int	help_paused_first_call = 0;
442 	u_char	*help_paused_path = NULL;
443 	u_char	*help_paused_name = NULL;
444 	u_char	*temp;
445 	u_char	*tmp = NULL;
446 	StringList *sl;
447 
448 #ifdef ZCAT
449 	u_char	*arg_z = NULL;
450 #endif /* ZCAT */
451 
452 	Debug(DB_HELP, "got topics '%s' args '%s'", topics, args);
453 	if (*topics)
454 		malloc_strcpy(&help_topic_list, topics);
455 	else
456 		new_free(&help_topic_list);
457 
458 #ifdef DAEMON_UID
459 	if (DAEMON_UID == getuid())
460 		ptr = DEFAULT_HELP_PATH;
461 	else
462 #endif /* DAEMON_UID */
463 		ptr = get_string_var(HELP_PATH_VAR);
464 
465 	snprintf(CP(path), sizeof path, "%s/%s", ptr, topics);
466 	for (ptr = path; (ptr = my_index(ptr, ' '));)
467 		*ptr = '/';
468 
469 
470 	/*
471 	 * first we check access to the help dir, whinge if we can't, then
472 	 * work out we need to ask them for more help, else we check the
473 	 * args list, and do the stuff
474 	 */
475 
476 	if (help_show_directory)
477 	{
478 		help_show_paused_topic(paused_topic, NULL);
479 		help_show_directory = 0;
480 	}
481 
482 	finished_help_paging = 0;
483 	if (access(CP(path), R_OK|X_OK))
484 	{
485 		help_put_it(no_help, "*** Cannot access help directory!");
486 		set_help_screen(NULL);
487 		return;
488 	}
489 
490 	this_arg = next_arg(args, &args);
491 	if (!this_arg && help_topic_list && get_int_var(HELP_PROMPT_VAR))
492 	{
493 		if ((temp = my_rindex(help_topic_list, ' ')) != NULL)
494 			*temp = '\0';
495 		else
496 			new_free(&help_topic_list);
497 		help_prompt_format_add();
498 		return;
499 	}
500 
501 	if (!this_arg)		/*  && *help_topic_list) */
502 	{
503 		set_help_screen(NULL);
504 		return;
505 	}
506 
507 	create_help_window();
508 	while (this_arg)
509 	{
510 		save_message_from();
511 		message_from(NULL, LOG_CURRENT);
512 		if (*this_arg == '\0')
513 			help_topic(path, NULL);
514 		if (my_strcmp(this_arg, "?") == 0)
515 		{
516 			this_arg = empty_string();
517 			if (!dont_pause_topic)
518 				dont_pause_topic = 1;
519 		}
520 		entry_size = 0;
521 
522 		/*
523 		 * here we clean the namelist if it exists, and then go to
524 		 * work on the directory.. working out if is dead, or if we
525 		 * can show some help, or create the paused topic list.
526 		 */
527 
528 		if (namelist)
529 		{
530 			for (i = 0; i < free_cnt; i++)
531 				new_free(&(namelist[i]));
532 			new_free(&namelist);
533 		}
534 		free_cnt = entries = scandir(CP(path), &namelist,
535 						 selectent, compar);
536 		/* special case to handle stuff like LOG and LOGFILE */
537 		if (entries > 1)
538 		{
539 #ifdef ZCAT
540 			/* Check if exist compressed or uncompressed entries */
541 			malloc_strcpy(&arg_z, this_arg);
542 			malloc_strcat(&arg_z, UP(ZSUFFIX));
543 			if (my_stricmp(UP(namelist[0]->d_name), arg_z) == 0 ||
544 				my_stricmp(UP(namelist[0]->d_name), this_arg) == 0)
545 #else
546 			if (my_stricmp(UP(namelist[0]->d_name), this_arg) == 0)
547 #endif /* ZCAT */
548 				entries = 1;
549 #ifdef ZCAT
550 			new_free(&arg_z);
551 #endif /* ZCAT */
552 		}
553 
554 		/*
555 		 * entries: -1 means something really died, 0 means there
556 		 * was no help, 1, means it wasn't a directory, and so to
557 		 * show the help file, and the default means to add the
558 		 * stuff to the paused topic list..
559 		 */
560 
561 		if (help_topic_list)
562 			dont_pause_topic = 1;
563 		switch (entries)
564 		{
565 		case -1:
566 			help_put_it(no_help, "*** Error during help function: %s",
567 				    strerror(errno));
568 			set_help_screen(NULL);
569 			if (help_paused_first_call)
570 			{
571 				help_topic(help_paused_path, help_paused_name);
572 				help_paused_first_call = 0;
573 				new_free(&help_paused_path);
574 				new_free(&help_paused_name);
575 			}
576 			return;
577 		case 0:
578 			help_no_help(this_arg);
579 			if (!get_int_var(HELP_PROMPT_VAR))
580 			{
581 				set_help_screen(NULL);
582 				break;
583 			}
584 			help_prompt_format_add();
585 			if (help_paused_first_call)
586 			{
587 				help_topic(help_paused_path, help_paused_name);
588 				help_paused_first_call = 0;
589 				new_free(&help_paused_path);
590 				new_free(&help_paused_name);
591 			}
592 			for (i = 0; i < free_cnt; i++)
593 			{
594 				new_free(&namelist[i]);
595 			}
596 			break;
597 		case 1:
598 			malloc_snprintf(&tmp, "%s/%s", path, namelist[0]->d_name);
599 			if (stat(CP(tmp), &sb) == -1)
600 			{
601 				for (i = 0; i < free_cnt; i++)
602 				{
603 					new_free(&namelist[i]);
604 				}
605 				new_free(&tmp);
606 				restore_message_from();
607 				continue;
608 			}
609 			if (sb.st_mode & S_IFDIR)
610 			{
611 				my_strmcpy(path, tmp, sizeof path);
612 				if (help_topic_list)
613 					malloc_snprintf(&help_topic_list,
614 							"%s %s",
615 							help_topic_list,
616 							namelist[0]->d_name);
617 				else
618 					malloc_strcpy(&help_topic_list,
619 						      UP(namelist[0]->d_name));
620 				Debug(DB_HELP, "new topic list: '%s'", help_topic_list);
621 
622 				if ((this_arg = next_arg(args, &args)) == NULL)
623 				{
624 					help_paused_first_call = 1;
625 					malloc_strcpy(&help_paused_path, path);
626 					malloc_strcpy(&help_paused_name,
627 						UP(namelist[0]->d_name));
628 					dont_pause_topic = -1;
629 					this_arg = UP("?");
630 				}
631 				for (i = 0; i < free_cnt; i++)
632 				{
633 					new_free(&namelist[i]);
634 				}
635 				new_free(&tmp);
636 				restore_message_from();
637 				continue;
638 			}
639 			help_topic(path, UP(namelist[0]->d_name));
640 			finished_help_paging = 0;	/* this is a big kludge */
641 			for (i = 0; i < free_cnt; i++)
642 			{
643 				new_free(&namelist[i]);
644 			}
645 			new_free(&tmp);
646 			break;
647 		default:
648 			help_show_directory = 1;
649 			my_strmcpy(paused_topic, help_topic_list ? help_topic_list : empty_string(), sizeof paused_topic);
650 			help_pause_add_line("*** %s choices:", paused_topic);
651 			cnt = 0;
652 			sl = sl_init();
653 			entry_size += 2;
654 			cols = (get_co() - 10) / entry_size;
655 			for (i = 0; i < entries; i++)
656 			{
657 				u_char	*buffer = NULL;
658 
659 				malloc_snprintf(&buffer, "%-13s", namelist[i]->d_name);
660 #ifdef ZCAT
661 				/*
662 				 * In tmp store the actual help choice and strip
663 				 * .Z suffix in compressed files: put filename
664 				 * (without .Z) on the help screen.  If it is
665 				 * the first choice cat it to the buffer and
666 				 * save the last choice.
667 				 */
668 				temp = &(buffer[my_strlen(buffer) - my_strlen(ZSUFFIX)]);
669 				if (!my_strcmp(temp, ZSUFFIX))
670 					temp[0] = '\0';
671 #endif /* ZCAT */
672 				sl_add(sl, CP(buffer));
673 				if (++cnt == cols)
674 				{
675 					buffer = sl_concat(sl, empty_string());
676 					help_pause_add_line("%s", buffer);
677 					new_free(&buffer);
678 					sl_free(sl, 1);
679 					sl = sl_init();
680 					cnt = 0;
681 				}
682 			}
683 			if (sl_size(sl) != 0)
684 			{
685 				u_char	*buffer = sl_concat(sl, empty_string());
686 				help_pause_add_line("%s", buffer);
687 				new_free(&buffer);
688 			}
689 			sl_free(sl, 1);
690 			if (help_paused_first_call)
691 			{
692 				help_topic(help_paused_path, help_paused_name);
693 				help_paused_first_call = 0;
694 				new_free(&help_paused_path);
695 				new_free(&help_paused_name);
696 			}
697 			if (dont_pause_topic == 1)
698 			{
699 				help_show_paused_topic(paused_topic, NULL);
700 				help_show_directory = 0;
701 			}
702 			break;
703 		}
704 		for (i = 0; i < free_cnt; i++)
705 		{
706 			new_free(&namelist[i]);
707 		}
708 		new_free(&namelist);
709 		restore_message_from();
710 		break;
711 	}
712 	/*
713 	 * This one is for when there was never a topic and the prompt
714 	 * never got a topic..  and help_screen was never reset..
715 	 * phone, jan 1993.
716 	 */
717 	if (help_topic_list && finished_help_paging)
718 		set_help_screen(NULL);
719 }
720 
721 /*
722  * help: the HELP command, gives help listings for any and all topics out
723  * there
724  */
725 void
help(u_char * command,u_char * args,u_char * subargs)726 help(u_char *command, u_char *args, u_char *subargs)
727 {
728 	u_char	*help_path;
729 
730 	finished_help_paging = 0;
731 	help_show_directory = 0;
732 	dont_pause_topic = 0;
733 	use_help_window = 0;
734 
735 #ifdef DAEMON_UID
736 	if (DAEMON_UID == getuid())
737 		help_path = DEFAULT_HELP_PATH;
738 	else
739 #endif /* DAEMON_UID */
740 		help_path = get_string_var(HELP_PATH_VAR);
741 	if (!(help_path && *help_path && !access(CP(help_path), R_OK | X_OK)))
742 	{
743 		if (*help_path)
744 			help_put_it(no_help, "*** HELP_PATH error: %s", strerror(errno));
745 		else
746 			help_put_it(no_help, "*** No HELP_PATH variable set");
747 		return;
748 	}
749 
750 	/*
751 	 * Check that we aren't doing HELP in a more than one screen, as this
752 	 * really leads to seriously difficult to fix problems!.  This is all
753 	 * due to the wildly popular /window create.  blah.
754 	 */
755 	if (help_path && help_screen && help_screen != get_current_screen())
756 	{
757 		say("You may not run help in two screens");
758 		return;
759 	}
760 	help_screen = get_current_screen();
761 	help_window = NULL;
762 	help_me(empty_string(), (args && *args) ? args : (u_char *) "?");
763 }
764 
765 static	void
create_help_window(void)766 create_help_window(void)
767 {
768 	if (help_window)
769 		return;
770 
771 	if (!term_basic() && get_int_var(HELP_WINDOW_VAR))
772 	{
773 		use_help_window = 1;
774 		help_window = new_window();
775 		window_set_hold_mode(help_window, OFF);
776 		update_all_windows();
777 	}
778 	else
779 		help_window = curr_scr_win;
780 }
781 
782 static	void
set_help_screen(Screen * screen)783 set_help_screen(Screen *screen)
784 {
785 	help_screen = screen;
786 	if (!help_screen && help_window)
787 	{
788 		if (use_help_window)
789 		{
790 			unsigned display = set_display_off();
791 			delete_window(help_window);
792 			set_display(display);
793 		}
794 		help_window = NULL;
795 		update_all_windows();
796 	}
797 }
798