Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
BitchX
GitHub Repository: BitchX/BitchX1.3
Path: blob/master/source/help.c
1069 views
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 3 2008-02-25 09:49:14Z keaston $";
28
#endif
29
30
#include "irc.h"
31
static char cvsrevision[] = "$Id: help.c 3 2008-02-25 09:49:14Z keaston $";
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[128];
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
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
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
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
765