1 /*
2 * help.c
3 *
4 *
5 *
6 * Revision history:
7 *
8 * 2-26-90 EAN Initial version.
9 *
10 *
11 */
12
13 #ifndef TEST /* kills all those assert macros in production version */
14 #define NDEBUG
15 #endif
16
17 #define INCLUDE_COMMON /* include common code in helpcom.h */
18
19 #ifndef XFRACT
20 #include <io.h>
21 #endif
22 #include <fcntl.h>
23 #include <string.h>
24 #include <time.h>
25 #include <assert.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 /* see Fractint.c for a description of the "include" hierarchy */
29 #include "port.h"
30 #include "prototyp.h"
31 #include "helpdefs.h"
32
33 #define MAX_HIST 16 /* number of pages we'll remember */
34 #define ALT_F1 1104
35 #define ACTION_CALL 0 /* values returned by help_topic() */
36 #define ACTION_PREV 1
37 #define ACTION_PREV2 2 /* special - go back two topics */
38 #define ACTION_INDEX 3
39 #define ACTION_QUIT 4
40 #define F_HIST (1<<0) /* flags for help_topic() */
41 #define F_INDEX (1<<1)
42 #define MAX_PAGE_SIZE (80*25) /* no page of text may be larger */
43 #define TEXT_START_ROW 2 /* start print the help text here */
44
45 typedef struct
46 {
47 BYTE r, c;
48 int width;
49 unsigned offset;
50 int topic_num;
51 unsigned topic_off;
52 } LINK;
53
54 typedef struct
55 {
56 int topic_num;
57 unsigned topic_off;
58 } LABEL;
59
60 typedef struct
61 {
62 unsigned offset;
63 unsigned len;
64 int margin;
65 } PAGE;
66
67 typedef struct
68 {
69 int topic_num;
70 unsigned topic_off;
71 int link;
72 } HIST;
73
74 struct help_sig_info
75 {
76 unsigned long sig;
77 int version;
78 unsigned long base; /* only if added to fractint.exe */
79 } ;
80
81 void print_document(char *outfname, int (*msg_func)(int,int), int save_extraseg );
82 static int print_doc_msg_func(int pnum, int num_pages);
83
84 /* stuff from fractint */
85
86 static int help_file = -1; /* help file handle */
87 static long base_off; /* offset to help info in help file */
88 static int max_links; /* max # of links in any page */
89 static int max_pages; /* max # of pages in any topic */
90 static int num_label; /* number of labels */
91 static int num_topic; /* number of topics */
92 static int curr_hist = 0; /* current pos in history */
93
94 /* these items alloc'ed in init_help... */
95
96 static long far *topic_offset; /* 4*num_topic */
97 static LABEL far *label; /* 4*num_label */
98 static HIST far *hist; /* 6*MAX_HIST (96 bytes) */
99
100 /* these items alloc'ed only while help is active... */
101
102 static char far *buffer; /* MAX_PAGE_SIZE (2048 bytes) */
103 static LINK far *link_table; /* 10*max_links */
104 static PAGE far *page_table; /* 4*max_pages */
105
help_seek(long pos)106 static void help_seek(long pos)
107 {
108 lseek(help_file, base_off+pos, SEEK_SET);
109 }
110
displaycc(int row,int col,int color,int ch)111 static void displaycc(int row, int col, int color, int ch)
112 {
113 #ifndef XFRACT
114 static char *s = "?";
115 #else
116 static char s[] = "?";
117 #endif
118
119 if (text_type == 1) /* if 640x200x2 mode */
120 {
121 /*
122 * This is REALLY ugly, but it works. Non-current links (ones that
123 * would be bold if 640x200 supported it) are in upper-case and the
124 * current item is inversed.
125 *
126 */
127
128 if (color & INVERSE) /* active link */
129 color = (signed int)INVERSE;
130 else if (color & BRIGHT) /* inactive link */
131 {
132 color = 0; /* normal */
133 if (ch>='a' && ch<='z')
134 ch += 'A' - 'a';
135 }
136 else /* normal */
137 color = 0;
138 }
139
140 s[0] = (char)ch;
141 putstring(row, col, color, s);
142 }
143
display_text(int row,int col,int color,char far * text,unsigned len)144 static void display_text(int row, int col, int color, char far *text, unsigned len)
145 {
146 while (len-- != 0)
147 {
148 if (*text == CMD_LITERAL)
149 {
150 ++text;
151 --len;
152 }
153 displaycc(row, col++, color, *text++);
154 }
155 }
156
display_parse_text(char far * text,unsigned len,int start_margin,int * num_link,LINK far * link)157 static void display_parse_text(char far *text, unsigned len, int start_margin, int *num_link, LINK far *link)
158 {
159 char far *curr;
160 int row, col;
161 int tok;
162 int size,
163 width;
164
165 textcbase = SCREEN_INDENT;
166 textrbase = TEXT_START_ROW;
167
168 curr = text;
169 row = 0;
170 col = 0;
171
172 size = width = 0;
173
174 if (start_margin >= 0)
175 tok = TOK_PARA;
176 else
177 tok = -1;
178
179 for(;;)
180 {
181 switch ( tok )
182 {
183 case TOK_PARA:
184 {
185 int indent,
186 margin;
187
188 if (size > 0)
189 {
190 ++curr;
191 indent = *curr++;
192 margin = *curr++;
193 len -= 3;
194 }
195 else
196 {
197 indent = start_margin;
198 margin = start_margin;
199 }
200
201 col = indent;
202
203 for(;;)
204 {
205 tok = find_token_length(ONLINE, curr, len, &size, &width);
206
207 if (tok == TOK_DONE || tok == TOK_NL || tok == TOK_FF )
208 break;
209
210 if (tok == TOK_PARA)
211 {
212 col = 0; /* fake a new-line */
213 row++;
214 break;
215 }
216
217 if (tok == TOK_XONLINE || tok == TOK_XDOC)
218 {
219 curr += size;
220 len -= size;
221 continue;
222 }
223
224 /* now tok is TOK_SPACE or TOK_LINK or TOK_WORD */
225
226 if (col+width > SCREEN_WIDTH)
227 { /* go to next line... */
228 col = margin;
229 ++row;
230
231 if ( tok == TOK_SPACE )
232 width = 0; /* skip spaces at start of a line */
233 }
234
235 if (tok == TOK_LINK)
236 {
237 display_text(row, col, C_HELP_LINK, curr+1+3*sizeof(int), width);
238 if (num_link != NULL)
239 {
240 link[*num_link].r = (BYTE)row;
241 link[*num_link].c = (BYTE)col;
242 link[*num_link].topic_num = getint(curr+1);
243 link[*num_link].topic_off = getint(curr+1+sizeof(int));
244 link[*num_link].offset = (unsigned) ((curr+1+3*sizeof(int)) - text);
245 link[*num_link].width = width;
246 ++(*num_link);
247 }
248 }
249 else if (tok == TOK_WORD )
250 display_text(row, col, C_HELP_BODY, curr, width);
251
252 col += width;
253 curr += size;
254 len -= size;
255 }
256
257 width = size = 0;
258 break;
259 }
260
261 case TOK_CENTER:
262 col = find_line_width(ONLINE, curr, len);
263 col = (SCREEN_WIDTH-col)/2;
264 if (col < 0)
265 col = 0;
266 break;
267
268 case TOK_NL:
269 col = 0;
270 ++row;
271 break;
272
273 case TOK_LINK:
274 display_text(row, col, C_HELP_LINK, curr+1+3*sizeof(int), width);
275 if (num_link != NULL)
276 {
277 link[*num_link].r = (BYTE)row;
278 link[*num_link].c = (BYTE)col;
279 link[*num_link].topic_num = getint(curr+1);
280 link[*num_link].topic_off = getint(curr+1+sizeof(int));
281 link[*num_link].offset = (unsigned) ((curr+1+3*sizeof(int)) - text);
282 link[*num_link].width = width;
283 ++(*num_link);
284 }
285 break;
286
287 case TOK_XONLINE: /* skip */
288 case TOK_FF: /* ignore */
289 case TOK_XDOC: /* ignore */
290 case TOK_DONE:
291 case TOK_SPACE:
292 break;
293
294 case TOK_WORD:
295 display_text(row, col, C_HELP_BODY, curr, width);
296 break;
297 } /* switch */
298
299 curr += size;
300 len -= size;
301 col += width;
302
303 if (len == 0)
304 break;
305
306 tok = find_token_length(ONLINE, curr, len, &size, &width);
307 } /* for(;;) */
308
309 textcbase = 0;
310 textrbase = 0;
311 }
312
color_link(LINK far * link,int color)313 static void color_link(LINK far *link, int color)
314 {
315 textcbase = SCREEN_INDENT;
316 textrbase = TEXT_START_ROW;
317
318 if (text_type == 1) /* if 640x200x2 mode */
319 display_text(link->r, link->c, color, buffer+link->offset, link->width);
320 else
321 setattr(link->r, link->c, color, link->width);
322
323 textcbase = 0;
324 textrbase = 0;
325 }
326
327 /* #define PUT_KEY(name, descrip) putstring(-1,-1,C_HELP_INSTR_KEYS,name), putstring(-1,-1,C_HELP_INSTR," "descrip" ") */
328 #ifndef XFRACT
329 #define PUT_KEY(name, descrip) putstring(-1,-1,C_HELP_INSTR,name); putstring(-1,-1,C_HELP_INSTR,":"descrip" ")
330 #else
331 #define PUT_KEY(name, descrip) putstring(-1,-1,C_HELP_INSTR,name);\
332 putstring(-1,-1,C_HELP_INSTR,":");\
333 putstring(-1,-1,C_HELP_INSTR,descrip);\
334 putstring(-1,-1,C_HELP_INSTR," ")
335 #endif
336
helpinstr(void)337 static void helpinstr(void)
338 {
339 int ctr;
340
341 for (ctr=0; ctr<80; ctr++)
342 putstring(24, ctr, C_HELP_INSTR, " ");
343
344 movecursor(24, 1);
345 PUT_KEY("F1", "Index");
346 #ifndef XFRACT
347 PUT_KEY("\030\031\033\032", "Select");
348 #else
349 PUT_KEY("K J H L", "Select");
350 #endif
351 PUT_KEY("Enter", "Go to");
352 PUT_KEY("Backspace", "Last topic");
353 PUT_KEY("Escape", "Exit help");
354 }
355
printinstr(void)356 static void printinstr(void)
357 {
358 int ctr;
359
360 for (ctr=0; ctr<80; ctr++)
361 putstring(24, ctr, C_HELP_INSTR, " ");
362
363 movecursor(24, 1);
364 PUT_KEY("Escape", "Abort");
365 }
366
367 #undef PUT_KEY
368
display_page(char far * title,char far * text,unsigned text_len,int page,int num_pages,int start_margin,int * num_link,LINK far * link)369 static void display_page(char far *title, char far *text, unsigned text_len, int page, int num_pages, int start_margin, int *num_link, LINK far *link)
370 {
371 char temp[9];
372
373 helptitle();
374 helpinstr();
375 setattr(2, 0, C_HELP_BODY, 80*22);
376 putstringcenter(1, 0, 80, C_HELP_HDG, title);
377 sprintf(temp, "%2d of %d", page+1, num_pages);
378 #ifndef XFRACT
379 putstring(1, 79-(6 + ((num_pages>=10)?2:1)), C_HELP_INSTR, temp);
380 #else
381 /* Some systems (Ultrix) mess up if you write to column 80 */
382 putstring(1, 78-(6 + ((num_pages>=10)?2:1)), C_HELP_INSTR, temp);
383 #endif
384
385 if (text != NULL)
386 display_parse_text(text, text_len, start_margin, num_link, link);
387
388 movecursor(25, 80); /* hide cursor */
389 }
390
391 /*
392 * int overlap(int a, int a2, int b, int b2);
393 *
394 * If a, a2, b, and b2 are points on a line, this function returns the
395 * distance of intersection between a-->a2 and b-->b2. If there is no
396 * intersection between the lines this function will return a negative number
397 * representing the distance between the two lines.
398 *
399 * There are six possible cases of intersection between the lines:
400 *
401 * a a2
402 * | |
403 * b b2 | | b b2
404 * |---(1)---| | | |---(2)---|
405 * | |
406 * b | b2 b | b2
407 * |------(3)----| |------(4)-----|
408 * | |
409 * b | | b2
410 * |------+--------(5)----------+---|
411 * | |
412 * | b b2 |
413 * | |--(6)--| |
414 * | |
415 * | |
416 *
417 */
418
overlap(int a,int a2,int b,int b2)419 static int overlap(int a, int a2, int b, int b2)
420 {
421 if ( b < a )
422 {
423 if ( b2 >= a2 )
424 return ( a2 - a ); /* case (5) */
425
426 return ( b2 - a ); /* case (1), case (3) */
427 }
428
429 if ( b2 <= a2 )
430 return ( b2 - b ); /* case (6) */
431
432 return ( a2 - b ); /* case (2), case (4) */
433 }
434
dist1(int a,int b)435 static int dist1(int a, int b)
436 {
437 int t = a - b;
438
439 return (abs(t));
440 }
441
442 #ifdef __TURBOC__
443 # pragma warn -def /* turn off "Possible use before definition" warning */
444 #endif
445
find_link_updown(LINK far * link,int num_link,int curr_link,int up)446 static int find_link_updown(LINK far *link, int num_link, int curr_link, int up)
447 {
448 int ctr,
449 curr_c2,
450 best_overlap = 0,
451 temp_overlap;
452 LINK far *curr,
453 far *temp,
454 far *best;
455 int temp_dist;
456
457 curr = &link[curr_link];
458 best = NULL;
459 curr_c2 = curr->c + curr->width - 1;
460
461 for (ctr=0, temp=link; ctr<num_link; ctr++, temp++)
462 {
463 if ( ctr != curr_link &&
464 ( (up && temp->r < curr->r) || (!up && temp->r > curr->r) ) )
465 {
466 temp_overlap = overlap(curr->c, curr_c2, temp->c, temp->c+temp->width-1);
467 /* if >= 3 lines between, prioritize on vertical distance: */
468 if ((temp_dist = dist1(temp->r, curr->r)) >= 4)
469 temp_overlap -= temp_dist * 100;
470
471 if (best != NULL)
472 {
473 if ( best_overlap >= 0 && temp_overlap >= 0 )
474 { /* if they're both under curr set to closest in y dir */
475 if ( dist1(best->r, curr->r) > temp_dist )
476 best = NULL;
477 }
478 else
479 {
480 if ( best_overlap < temp_overlap )
481 best = NULL;
482 }
483 }
484
485 if (best == NULL)
486 {
487 best = temp;
488 best_overlap = temp_overlap;
489 }
490 }
491 }
492
493 return ( (best==NULL) ? -1 : (int)(best-link) );
494 }
495
find_link_leftright(LINK far * link,int num_link,int curr_link,int left)496 static int find_link_leftright(LINK far *link, int num_link, int curr_link, int left)
497 {
498 int ctr,
499 curr_c2,
500 best_c2 = 0,
501 temp_c2,
502 best_dist = 0,
503 temp_dist;
504 LINK far *curr,
505 far *temp,
506 far *best;
507
508 curr = &link[curr_link];
509 best = NULL;
510 curr_c2 = curr->c + curr->width - 1;
511
512 for (ctr=0, temp=link; ctr<num_link; ctr++, temp++)
513 {
514 temp_c2 = temp->c + temp->width - 1;
515
516 if ( ctr != curr_link &&
517 ( (left && temp_c2 < (int)curr->c) || (!left && (int)temp->c > curr_c2) ) )
518 {
519 temp_dist = dist1(curr->r, temp->r);
520
521 if (best != NULL)
522 {
523 if ( best_dist == 0 && temp_dist == 0 ) /* if both on curr's line... */
524 {
525 if ( ( left && dist1(curr->c, best_c2) > dist1(curr->c, temp_c2) ) ||
526 ( !left && dist1(curr_c2, best->c) > dist1(curr_c2, temp->c) ) )
527 best = NULL;
528 }
529 else
530 {
531 if ( best_dist >= temp_dist ) /* if temp is closer... */
532 best = NULL;
533 }
534 } /* if (best...) */
535
536 if (best == NULL)
537 {
538 best = temp;
539 best_dist = temp_dist;
540 best_c2 = temp_c2;
541 }
542 }
543 } /* for */
544
545 return ( (best==NULL) ? -1 : (int)(best-link) );
546 }
547
548 #ifdef __TURBOC__
549 # pragma warn .def /* back to default */
550 # pragma warn -par /* now turn off "Parameter not used" warning */
551 #endif
552
553 #ifdef __CLINT__
554 # pragma argsused
555 #endif
556
find_link_key(LINK far * link,int num_link,int curr_link,int key)557 static int find_link_key(LINK far *link, int num_link, int curr_link, int key)
558 {
559 link = NULL; /* just for warning */
560 switch (key)
561 {
562 case TAB: return ( (curr_link>=num_link-1) ? -1 : curr_link+1 );
563 case BACK_TAB: return ( (curr_link<=0) ? -1 : curr_link-1 );
564 default: assert(0); return (-1);
565 }
566 }
567
568 #ifdef __TURBOC__
569 # pragma warn .par /* back to default */
570 #endif
571
do_move_link(LINK far * link,int num_link,int * curr,int (* f)(LINK far *,int,int,int),int val)572 static int do_move_link(LINK far *link, int num_link, int *curr, int (*f)(LINK far *,int,int,int), int val)
573 {
574 int t;
575
576 if (num_link > 1)
577 {
578 if ( f == NULL )
579 t = val;
580 else
581 t = (*f)(link, num_link, *curr, val);
582
583 if ( t >= 0 && t != *curr )
584 {
585 color_link(&link[*curr], C_HELP_LINK);
586 *curr = t;
587 color_link(&link[*curr], C_HELP_CURLINK);
588 return (1);
589 }
590 }
591
592 return (0);
593 }
594
help_topic(HIST * curr,HIST * next,int flags)595 static int help_topic(HIST *curr, HIST *next, int flags)
596 {
597 int len;
598 int key;
599 int num_pages;
600 int num_link;
601 int page;
602 int curr_link;
603 char title[81];
604 long where;
605 int draw_page;
606 int action;
607 BYTE ch;
608 int dummy; /* to quiet compiler */
609
610 where = topic_offset[curr->topic_num]+sizeof(int); /* to skip flags */
611 curr_link = curr->link;
612
613 help_seek(where);
614
615 dummy = read(help_file, (char *)&num_pages, sizeof(int));
616 assert(num_pages>0 && num_pages<=max_pages);
617
618 farread(help_file, (char far *)page_table, 3*sizeof(int)*num_pages);
619
620 dummy = read(help_file, &ch, 1);
621 len = ch;
622 assert(len<81);
623 dummy = read(help_file, (char *)title, len);
624 title[len] = '\0';
625
626 where += sizeof(int) + num_pages*3*sizeof(int) + 1 + len + sizeof(int);
627
628 for(page=0; page<num_pages; page++)
629 if (curr->topic_off >= page_table[page].offset &&
630 curr->topic_off < page_table[page].offset+page_table[page].len )
631 break;
632
633 assert(page < num_pages);
634
635 action = -1;
636 draw_page = 2;
637
638 do
639 {
640 if (draw_page)
641 {
642 help_seek(where+page_table[page].offset);
643 farread(help_file, buffer, page_table[page].len);
644
645 num_link = 0;
646 display_page(title, buffer, page_table[page].len, page, num_pages,
647 page_table[page].margin, &num_link, link_table);
648
649 if (draw_page==2)
650 {
651 assert(num_link<=0 || (curr_link>=0 && curr_link<num_link));
652 }
653 else if (draw_page==3)
654 curr_link = num_link - 1;
655 else
656 curr_link = 0;
657
658 if (num_link > 0)
659 color_link(&link_table[curr_link], C_HELP_CURLINK);
660
661 draw_page = 0;
662 }
663
664 key = getakey();
665
666 switch(key)
667 {
668 case PAGE_DOWN:
669 if (page<num_pages-1)
670 {
671 page++;
672 draw_page = 1;
673 }
674 break;
675
676 case PAGE_UP:
677 if (page>0)
678 {
679 page--;
680 draw_page = 1;
681 }
682 break;
683
684 case HOME:
685 if ( page != 0 )
686 {
687 page = 0;
688 draw_page = 1;
689 }
690 else
691 do_move_link(link_table, num_link, &curr_link, NULL, 0);
692 break;
693
694 case END:
695 if ( page != num_pages-1 )
696 {
697 page = num_pages-1;
698 draw_page = 3;
699 }
700 else
701 do_move_link(link_table, num_link, &curr_link, NULL, num_link-1);
702 break;
703
704 case TAB:
705 if ( !do_move_link(link_table, num_link, &curr_link, find_link_key, key) &&
706 page<num_pages-1 )
707 {
708 ++page;
709 draw_page = 1;
710 }
711 break;
712
713 case BACK_TAB:
714 if ( !do_move_link(link_table, num_link, &curr_link, find_link_key, key) &&
715 page>0 )
716 {
717 --page;
718 draw_page = 3;
719 }
720 break;
721
722 case DOWN_ARROW:
723 if ( !do_move_link(link_table, num_link, &curr_link, find_link_updown, 0) &&
724 page<num_pages-1 )
725 {
726 ++page;
727 draw_page = 1;
728 }
729 break;
730
731 case UP_ARROW:
732 if ( !do_move_link(link_table, num_link, &curr_link, find_link_updown, 1) &&
733 page>0 )
734 {
735 --page;
736 draw_page = 3;
737 }
738 break;
739
740 case LEFT_ARROW:
741 do_move_link(link_table, num_link, &curr_link, find_link_leftright, 1);
742 break;
743
744 case RIGHT_ARROW:
745 do_move_link(link_table, num_link, &curr_link, find_link_leftright, 0);
746 break;
747
748 case ESC: /* exit help */
749 action = ACTION_QUIT;
750 break;
751
752 case BACKSPACE: /* prev topic */
753 case ALT_F1:
754 if (flags & F_HIST)
755 action = ACTION_PREV;
756 break;
757
758 case F1: /* help index */
759 if (!(flags & F_INDEX))
760 action = ACTION_INDEX;
761 break;
762
763 case ENTER:
764 case ENTER_2:
765 if (num_link > 0)
766 {
767 next->topic_num = link_table[curr_link].topic_num;
768 next->topic_off = link_table[curr_link].topic_off;
769 action = ACTION_CALL;
770 }
771 break;
772 } /* switch */
773 }
774 while ( action == -1 );
775
776 curr->topic_off = page_table[page].offset;
777 curr->link = curr_link;
778
779 return (action);
780 }
781
help(int action)782 int help(int action)
783 {
784 static FCODE unknowntopic_msg[] = "Unknown Help Topic";
785 HIST curr;
786 int oldlookatmouse;
787 int oldhelpmode;
788 int flags;
789 HIST next;
790
791 if (helpmode == -1) /* is help disabled? */
792 {
793 return (0);
794 }
795
796 if (help_file == -1)
797 {
798 buzzer(2);
799 return (0);
800 }
801
802 buffer = (char far *)farmemalloc((long)MAX_PAGE_SIZE + sizeof(LINK)*max_links +
803 sizeof(PAGE)*max_pages);
804
805 if (buffer == NULL)
806 {
807 buzzer(2);
808 return (0);
809 }
810
811 link_table = (LINK far *)(&buffer[MAX_PAGE_SIZE]);
812 page_table = (PAGE far *)(&link_table[max_links]);
813
814 oldlookatmouse = lookatmouse;
815 lookatmouse = 0;
816 timer_start -= clock_ticks();
817 stackscreen();
818
819 if (helpmode >= 0)
820 {
821 next.topic_num = label[helpmode].topic_num;
822 next.topic_off = label[helpmode].topic_off;
823 }
824 else
825 {
826 next.topic_num = helpmode;
827 next.topic_off = 0;
828 }
829
830 oldhelpmode = helpmode;
831
832 if (curr_hist <= 0)
833 action = ACTION_CALL; /* make sure it isn't ACTION_PREV! */
834
835 do
836 {
837 switch(action)
838 {
839 case ACTION_PREV2:
840 if (curr_hist > 0)
841 curr = hist[--curr_hist];
842
843 /* fall-through */
844
845 case ACTION_PREV:
846 if (curr_hist > 0)
847 curr = hist[--curr_hist];
848 break;
849
850 case ACTION_QUIT:
851 break;
852
853 case ACTION_INDEX:
854 next.topic_num = label[HELP_INDEX].topic_num;
855 next.topic_off = label[HELP_INDEX].topic_off;
856
857 /* fall-through */
858
859 case ACTION_CALL:
860 curr = next;
861 curr.link = 0;
862 break;
863 } /* switch */
864
865 flags = 0;
866 if (curr.topic_num == label[HELP_INDEX].topic_num)
867 flags |= F_INDEX;
868 if (curr_hist > 0)
869 flags |= F_HIST;
870
871 if ( curr.topic_num >= 0 )
872 action = help_topic(&curr, &next, flags);
873 else
874 {
875 if ( curr.topic_num == -100 )
876 {
877 print_document("FRACTINT.DOC", print_doc_msg_func, 1);
878 action = ACTION_PREV2;
879 }
880
881 else if ( curr.topic_num == -101 )
882 action = ACTION_PREV2;
883
884 else
885 {
886 display_page(unknowntopic_msg, NULL, 0, 0, 1, 0, NULL, NULL);
887 action = -1;
888 while (action == -1)
889 {
890 switch (getakey())
891 {
892 case ESC: action = ACTION_QUIT; break;
893 case ALT_F1: action = ACTION_PREV; break;
894 case F1: action = ACTION_INDEX; break;
895 } /* switch */
896 } /* while */
897 }
898 } /* else */
899
900 if ( action != ACTION_PREV && action != ACTION_PREV2 )
901 {
902 if (curr_hist >= MAX_HIST)
903 {
904 int ctr;
905
906 for (ctr=0; ctr<MAX_HIST-1; ctr++)
907 hist[ctr] = hist[ctr+1];
908
909 curr_hist = MAX_HIST-1;
910 }
911 hist[curr_hist++] = curr;
912 }
913 }
914 while (action != ACTION_QUIT);
915
916 farmemfree((BYTE far *)buffer);
917
918 unstackscreen();
919 lookatmouse = oldlookatmouse;
920 helpmode = oldhelpmode;
921 timer_start += clock_ticks();
922
923 return(0);
924 }
925
926 #ifndef XFRACT
dos_version(void)927 static int dos_version(void)
928 {
929 union REGS r;
930
931 r.x.ax = 0x3000;
932 intdos(&r, &r);
933
934 return (r.h.al*100 + r.h.ah);
935 }
936
937 static char s_fractintexe[] = "FRACTINT.EXE";
938 #endif
939
can_read_file(char * path)940 static int can_read_file(char *path)
941 {
942 int handle;
943
944 #ifdef __TURBOC__
945 if ( (handle=open(path, O_RDONLY|O_DENYWRITE)) != -1)
946 #else
947 if ( (handle=open(path, O_RDONLY)) != -1)
948 #endif
949 {
950 close(handle);
951 return (1);
952 }
953 else
954 return (0);
955 }
956
957
exe_path(char * filename,char * path)958 static int exe_path(char *filename, char *path)
959 {
960 #ifndef XFRACT
961 char *ptr;
962
963 if (dos_version() >= 300) /* DOS version 3.00+ ? */
964 {
965 #ifdef __TURBOC__
966 strcpy(path, _argv[0]);
967 #else /* assume MSC */
968 extern char **__argv;
969 strcpy(path, __argv[0]); /* note: __argv may be undocumented in MSC */
970 #endif
971 if(strcmp(filename,s_fractintexe)==0)
972 if (can_read_file(path))
973 return (1);
974 ptr = strrchr(path, SLASHC);
975 if (ptr == NULL)
976 ptr = path;
977 else
978 ++ptr;
979 strcpy(ptr, filename);
980 return (1);
981 }
982
983 return (0);
984 #else
985 strcpy(path,SRCDIR);
986 strcat(path,"/");
987 strcat(path,filename);
988 return 1;
989 #endif
990 }
991
find_file(char * filename,char * path)992 static int find_file(char *filename, char *path)
993 {
994 if ( exe_path(filename, path) )
995 if( can_read_file(path))
996 return (1);
997 findpath(filename,path);
998 return ( (path[0]) ? 1 : 0);
999 }
1000
_read_help_topic(int topic,int off,int len,VOIDFARPTR buf)1001 static int _read_help_topic(int topic, int off, int len, VOIDFARPTR buf)
1002 {
1003 static int curr_topic = -1;
1004 static long curr_base;
1005 static int curr_len;
1006 int read_len;
1007
1008 if ( topic != curr_topic )
1009 {
1010 int t;
1011 char ch;
1012 int dummy; /* to quiet compiler */
1013
1014 curr_topic = topic;
1015
1016 curr_base = topic_offset[topic];
1017
1018 curr_base += sizeof(int); /* skip flags */
1019
1020 help_seek(curr_base);
1021 dummy = read(help_file, (char *)&t, sizeof(int)); /* read num_pages */
1022 curr_base += sizeof(int) + t*3*sizeof(int); /* skip page info */
1023
1024 if (t>0)
1025 help_seek(curr_base);
1026 dummy = read(help_file, &ch, 1); /* read title_len */
1027 t = ch;
1028 curr_base += 1 + t; /* skip title */
1029
1030 if (t>0)
1031 help_seek(curr_base);
1032 dummy = read(help_file, (char *)&curr_len, sizeof(int)); /* read topic len */
1033 curr_base += sizeof(int);
1034 }
1035
1036 read_len = (off+len > curr_len) ? curr_len - off : len;
1037
1038 if (read_len > 0)
1039 {
1040 help_seek(curr_base + off);
1041 farread(help_file, (char far *)buf, read_len);
1042 }
1043
1044 return ( curr_len - (off+len) );
1045 }
1046
read_help_topic(int label_num,int off,int len,VOIDFARPTR buf)1047 int read_help_topic(int label_num, int off, int len, VOIDFARPTR buf)
1048 /*
1049 * reads text from a help topic. Returns number of bytes from (off+len)
1050 * to end of topic. On "EOF" returns a negative number representing
1051 * number of bytes not read.
1052 */
1053 {
1054 int ret;
1055 ret = _read_help_topic(label[label_num].topic_num,
1056 label[label_num].topic_off + off, len, buf);
1057 return ( ret );
1058 }
1059
1060 #define PRINT_BUFFER_SIZE (32767) /* max. size of help topic in doc. */
1061 #define TEMP_FILE_NAME "HELP.$$$" /* temp file for storing extraseg */
1062 /* while printing document */
1063 #define MAX_NUM_TOPIC_SEC (10) /* max. number of topics under any */
1064 /* single section (CONTENT) */
1065
1066 typedef struct PRINT_DOC_INFO
1067 {
1068 int cnum; /* current CONTENT num */
1069 int tnum; /* current topic num */
1070
1071 long content_pos; /* current CONTENT item offset in file */
1072 int num_page; /* total number of pages in document */
1073
1074 int num_contents, /* total number of CONTENT entries */
1075 num_topic; /* number of topics in current CONTENT */
1076
1077 int topic_num[MAX_NUM_TOPIC_SEC]; /* topic_num[] for current CONTENT entry */
1078
1079 char far *buffer; /* text buffer */
1080
1081 char id[81]; /* buffer to store id in */
1082 char title[81]; /* buffer to store title in */
1083
1084 #ifndef XFRACT
1085 int (*msg_func)(int pnum, int num_page);
1086 #else
1087 int (*msg_func)();
1088 int pnum;
1089 #endif
1090
1091 FILE *file; /* file to sent output to */
1092 int margin; /* indent text by this much */
1093 int start_of_line; /* are we at the beginning of a line? */
1094 int spaces; /* number of spaces in a row */
1095 } PRINT_DOC_INFO;
1096
1097 void print_document(char *outfname, int (*msg_func)(int,int), int save_extraseg );
1098
printerc(PRINT_DOC_INFO * info,int c,int n)1099 static void printerc(PRINT_DOC_INFO *info, int c, int n)
1100 {
1101 while ( n-- > 0 )
1102 {
1103 if (c==' ')
1104 ++info->spaces;
1105
1106 else if (c=='\n' || c=='\f')
1107 {
1108 info->start_of_line = 1;
1109 info->spaces = 0; /* strip spaces before a new-line */
1110 fputc(c, info->file);
1111 }
1112
1113 else
1114 {
1115 if (info->start_of_line)
1116 {
1117 info->spaces += info->margin;
1118 info->start_of_line = 0;
1119 }
1120
1121 while (info->spaces > 0)
1122 {
1123 fputc(' ', info->file);
1124 --info->spaces;
1125 }
1126
1127 fputc(c, info->file);
1128 }
1129 }
1130 }
1131
printers(PRINT_DOC_INFO * info,char far * s,int n)1132 static void printers(PRINT_DOC_INFO *info, char far *s, int n)
1133 {
1134 if (n > 0)
1135 {
1136 while ( n-- > 0 )
1137 printerc(info, *s++, 1);
1138 }
1139 else
1140 {
1141 while ( *s != '\0' )
1142 printerc(info, *s++, 1);
1143 }
1144 }
1145
print_doc_get_info(int cmd,PD_INFO * pd,PRINT_DOC_INFO * info)1146 static int print_doc_get_info(int cmd, PD_INFO *pd, PRINT_DOC_INFO *info)
1147 {
1148 int t;
1149 BYTE ch;
1150 int dummy; /* to quiet compiler */
1151
1152 switch (cmd)
1153 {
1154 case PD_GET_CONTENT:
1155 if ( ++info->cnum >= info->num_contents )
1156 return (0);
1157
1158 help_seek( info->content_pos );
1159
1160 dummy = read(help_file, (char *)&t, sizeof(int)); /* read flags */
1161 info->content_pos += sizeof(int);
1162 pd->new_page = (t & 1) ? 1 : 0;
1163
1164 dummy = read(help_file, &ch, 1); /* read id len */
1165 t = ch;
1166 assert(t<80);
1167 dummy = read(help_file, (char *)info->id, t); /* read the id */
1168 info->content_pos += 1 + t;
1169 info->id[t] = '\0';
1170
1171 dummy = read(help_file, (char *)&ch, 1); /* read title len */
1172 t = ch;
1173 assert(t<80);
1174 dummy = read(help_file, (char *)info->title, t); /* read the title */
1175 info->content_pos += 1 + t;
1176 info->title[t] = '\0';
1177
1178 dummy = read(help_file, (char *)&ch, 1); /* read num_topic */
1179 t = ch;
1180 assert(t<MAX_NUM_TOPIC_SEC);
1181 dummy = read(help_file, (char *)info->topic_num, t*sizeof(int)); /* read topic_num[] */
1182 info->num_topic = t;
1183 info->content_pos += 1 + t*sizeof(int);
1184
1185 info->tnum = -1;
1186
1187 pd->id = info->id;
1188 pd->title = info->title;
1189 return (1);
1190
1191 case PD_GET_TOPIC:
1192 if ( ++info->tnum >= info->num_topic )
1193 return (0);
1194
1195 t = _read_help_topic(info->topic_num[info->tnum], 0, PRINT_BUFFER_SIZE, info->buffer);
1196
1197 assert(t <= 0);
1198
1199 pd->curr = info->buffer;
1200 pd->len = PRINT_BUFFER_SIZE + t; /* same as ...SIZE - abs(t) */
1201 return (1);
1202
1203 case PD_GET_LINK_PAGE:
1204 pd->i = getint(pd->s+sizeof(long));
1205 return ( (pd->i == -1) ? 0 : 1 );
1206
1207 case PD_RELEASE_TOPIC:
1208 return (1);
1209
1210 default:
1211 return (0);
1212 }
1213 }
1214
print_doc_output(int cmd,PD_INFO * pd,PRINT_DOC_INFO * info)1215 static int print_doc_output(int cmd, PD_INFO *pd, PRINT_DOC_INFO *info)
1216 {
1217 switch (cmd)
1218 {
1219 case PD_HEADING:
1220 {
1221 char line[81];
1222 char buff[40];
1223 int width = PAGE_WIDTH + PAGE_INDENT;
1224 int keep_going;
1225
1226 if ( info->msg_func != NULL )
1227 keep_going = (*info->msg_func)(pd->pnum, info->num_page);
1228 else
1229 keep_going = 1;
1230
1231 info->margin = 0;
1232
1233 memset(line, ' ', 81);
1234 sprintf(buff, "Fractint Version %d.%01d%c",release/100, (release%100)/10,
1235 ( (release%10) ? '0'+(release%10) : ' ') );
1236 memmove(line + ((width-(int)(strlen(buff))) / 2)-4, buff, strlen(buff));
1237
1238 sprintf(buff, "Page %d", pd->pnum);
1239 memmove(line + (width - (int)strlen(buff)), buff, strlen(buff));
1240
1241 printerc(info, '\n', 1);
1242 printers(info, line, width);
1243 printerc(info, '\n', 2);
1244
1245 info->margin = PAGE_INDENT;
1246
1247 return ( keep_going );
1248 }
1249
1250 case PD_FOOTING:
1251 info->margin = 0;
1252 printerc(info, '\f', 1);
1253 info->margin = PAGE_INDENT;
1254 return (1);
1255
1256 case PD_PRINT:
1257 printers(info, pd->s, pd->i);
1258 return (1);
1259
1260 case PD_PRINTN:
1261 printerc(info, *pd->s, pd->i);
1262 return (1);
1263
1264 case PD_PRINT_SEC:
1265 info->margin = TITLE_INDENT;
1266 if (pd->id[0] != '\0')
1267 {
1268 printers(info, pd->id, 0);
1269 printerc(info, ' ', 1);
1270 }
1271 printers(info, pd->title, 0);
1272 printerc(info, '\n', 1);
1273 info->margin = PAGE_INDENT;
1274 return (1);
1275
1276 case PD_START_SECTION:
1277 case PD_START_TOPIC:
1278 case PD_SET_SECTION_PAGE:
1279 case PD_SET_TOPIC_PAGE:
1280 case PD_PERIODIC:
1281 return (1);
1282
1283 default:
1284 return (0);
1285 }
1286 }
1287
print_doc_msg_func(int pnum,int num_pages)1288 static int print_doc_msg_func(int pnum, int num_pages)
1289 {
1290 char temp[10];
1291 int key;
1292
1293 if ( pnum == -1 ) /* successful completion */
1294 {
1295 static FCODE msg[] = {"Done -- Press any key"};
1296 buzzer(0);
1297 putstringcenter(7, 0, 80, C_HELP_LINK, msg);
1298 getakey();
1299 return (0);
1300 }
1301
1302 if ( pnum == -2 ) /* aborted */
1303 {
1304 static FCODE msg[] = {"Aborted -- Press any key"};
1305 buzzer(1);
1306 putstringcenter(7, 0, 80, C_HELP_LINK, msg);
1307 getakey();
1308 return (0);
1309 }
1310
1311 if (pnum == 0) /* initialization */
1312 {
1313 static FCODE msg[] = {"Generating FRACTINT.DOC"};
1314 helptitle();
1315 printinstr();
1316 setattr(2, 0, C_HELP_BODY, 80*22);
1317 putstringcenter(1, 0, 80, C_HELP_HDG, msg);
1318
1319 putstring(7, 30, C_HELP_BODY, "Completed:");
1320
1321 movecursor(25,80); /* hide cursor */
1322 }
1323
1324 sprintf(temp, "%d%%", (int)( (100.0 / num_pages) * pnum ) );
1325 putstring(7, 41, C_HELP_LINK, temp);
1326
1327 while ( keypressed() )
1328 {
1329 key = getakey();
1330 if ( key == ESC )
1331 return (0); /* user abort */
1332 }
1333
1334 return (1); /* AOK -- continue */
1335 }
1336
makedoc_msg_func(int pnum,int num_pages)1337 int makedoc_msg_func(int pnum, int num_pages)
1338 {
1339 if (pnum >= 0)
1340 {
1341 printf("\rcompleted %d%%", (int)( (100.0 / num_pages) * pnum ) );
1342 return (1);
1343 }
1344 if ( pnum == -2 )
1345 printf("\n*** aborted");
1346 printf("\n");
1347 return (0);
1348 }
1349
print_document(char * outfname,int (* msg_func)(int,int),int save_extraseg)1350 void print_document(char *outfname, int (*msg_func)(int,int), int save_extraseg )
1351 {
1352 static FCODE err_no_temp[] = "Unable to create temporary file.\n";
1353 static FCODE err_no_out[] = "Unable to create output file.\n";
1354 static FCODE err_badwrite[] = "Error writing temporary file.\n";
1355 static FCODE err_badread[] = "Error reading temporary file.\nSystem may be corrupt!\nSave your image and re-start FRACTINT!\n";
1356
1357 PRINT_DOC_INFO info;
1358 int success = 0;
1359 int temp_file = -1;
1360 char far *msg = NULL;
1361 int dummy; /* to quiet compiler */
1362
1363 info.buffer = MK_FP(extraseg, 0);
1364
1365 /* help_seek((long)sizeof(int)+sizeof(long)); Strange -- should be 8 -- CWM */
1366 help_seek(8L); /* indeed it should - Bert */
1367 dummy = read(help_file, (char *)&info.num_contents, sizeof(int));
1368 dummy = read(help_file, (char *)&info.num_page, sizeof(int));
1369
1370 info.cnum = info.tnum = -1;
1371 info.content_pos = sizeof(long)+4*sizeof(int) + num_topic*sizeof(long) + num_label*2*sizeof(int);
1372 info.msg_func = msg_func;
1373
1374 if ( msg_func != NULL )
1375 msg_func(0, info.num_page); /* initialize */
1376
1377 if ( save_extraseg )
1378 {
1379 if ( (temp_file=open(TEMP_FILE_NAME, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IREAD|S_IWRITE)) == -1 )
1380 {
1381 msg = err_no_temp;
1382 goto ErrorAbort;
1383 }
1384
1385 if ( farwrite(temp_file, info.buffer, PRINT_BUFFER_SIZE) != PRINT_BUFFER_SIZE )
1386 {
1387 msg = err_badwrite;
1388 goto ErrorAbort;
1389 }
1390 }
1391
1392 if ( (info.file = fopen(outfname, "wt")) == NULL )
1393 {
1394 msg = err_no_out;
1395 goto ErrorAbort;
1396 }
1397
1398 info.margin = PAGE_INDENT;
1399 info.start_of_line = 1;
1400 info.spaces = 0;
1401
1402 success = process_document((PD_FUNC)print_doc_get_info,
1403 (PD_FUNC)print_doc_output, &info);
1404 fclose(info.file);
1405
1406 if ( save_extraseg )
1407 {
1408 if ( lseek(temp_file, 0L, SEEK_SET) != 0L )
1409 {
1410 msg = err_badread;
1411 goto ErrorAbort;
1412 }
1413
1414 if ( farread(temp_file, info.buffer, PRINT_BUFFER_SIZE) != PRINT_BUFFER_SIZE )
1415 {
1416 msg = err_badread;
1417 goto ErrorAbort;
1418 }
1419 }
1420
1421 ErrorAbort:
1422 if (temp_file != -1)
1423 {
1424 close(temp_file);
1425 remove(TEMP_FILE_NAME);
1426 temp_file = -1;
1427 }
1428
1429 if ( msg != NULL )
1430 {
1431 helptitle();
1432 stopmsg(1, msg);
1433 }
1434
1435 else if ( msg_func != NULL )
1436 msg_func((success) ? -1 : -2, info.num_page );
1437 }
1438
init_help(void)1439 int init_help(void)
1440 {
1441 struct help_sig_info hs;
1442 char path[FILE_MAX_PATH+1];
1443 int dummy; /* to quiet compiler */
1444
1445 help_file = -1;
1446
1447 #ifndef WINFRACT
1448 #ifndef XFRACT
1449 if (help_file == -1) /* now look for help files in FRACTINT.EXE */
1450 {
1451 static FCODE err_no_open[] = "Help system was unable to open FRACTINT.EXE!\n";
1452 static FCODE err_no_exe[] = "Help system couldn't find FRACTINT.EXE!\n";
1453 static FCODE err_wrong_ver[] = "Wrong help version in FRACTINT.EXE!\n";
1454 /*
1455 static FCODE err_not_in_exe[] = "Help not found in FRACTINT.EXE!\n";
1456 */
1457
1458 if ( find_file(s_fractintexe, path) )
1459 {
1460 #ifdef __TURBOC__
1461 if ( (help_file = open(path, O_RDONLY|O_BINARY|O_DENYWRITE)) != -1 )
1462 #else
1463 if ( (help_file = open(path, O_RDONLY|O_BINARY)) != -1 )
1464 #endif
1465 {
1466 long help_offset;
1467
1468 for (help_offset = -((long)sizeof(hs)); help_offset >= -128L; help_offset--)
1469 {
1470 lseek(help_file, help_offset, SEEK_END);
1471 dummy = read(help_file, (char *)&hs, sizeof(hs));
1472 if (hs.sig == HELP_SIG) break;
1473 }
1474
1475 if ( hs.sig != HELP_SIG )
1476 {
1477 close(help_file);
1478 help_file = -1;
1479 /* (leave out the error message)
1480 stopmsg(1, err_not_in_exe);
1481 */
1482 }
1483
1484 else
1485 {
1486 if ( hs.version != HELP_VERSION )
1487 {
1488 close(help_file);
1489 help_file = -1;
1490 stopmsg(1, err_wrong_ver);
1491 }
1492 else
1493 base_off = hs.base;
1494
1495 }
1496 }
1497 else
1498 stopmsg(1, err_no_open);
1499 }
1500 else
1501 stopmsg(1, err_no_exe);
1502
1503 }
1504 #endif
1505 #endif
1506
1507 if (help_file == -1) /* look for FRACTINT.HLP */
1508 {
1509 if ( find_file("fractint.hlp", path) )
1510 {
1511 #ifdef __TURBOC__
1512 if ( (help_file = open(path, O_RDONLY|O_BINARY|O_DENYWRITE)) != -1 )
1513 #else
1514 if ( (help_file = open(path, O_RDONLY|O_BINARY)) != -1 )
1515 #endif
1516 {
1517 dummy = read(help_file, (char *)&hs, sizeof(long)+sizeof(int));
1518
1519 if ( hs.sig != HELP_SIG )
1520 {
1521 static FCODE msg[] = {"Invalid help signature in FRACTINT.HLP!\n"};
1522 close(help_file);
1523 stopmsg(1, msg);
1524 }
1525
1526 else if ( hs.version != HELP_VERSION )
1527 {
1528 static FCODE msg[] = {"Wrong help version in FRACTINT.HLP!\n"};
1529 close(help_file);
1530 stopmsg(1, msg);
1531 }
1532
1533 else
1534 base_off = sizeof(long)+sizeof(int);
1535 }
1536 }
1537 }
1538
1539 if (help_file == -1) /* Can't find the help files anywhere! */
1540 {
1541 static FCODE msg[] =
1542 #ifndef XFRACT
1543 {"Help Files aren't in FRACTINT.EXE, and couldn't find FRACTINT.HLP!\n"};
1544 #else
1545 {"Couldn't find fractint.hlp; set FRACTDIR to proper directory with setenv.\n"};
1546 #endif
1547 stopmsg(1, msg);
1548 }
1549
1550 help_seek(0L);
1551
1552 dummy = read(help_file, (char *)&max_pages, sizeof(int));
1553 dummy = read(help_file, (char *)&max_links, sizeof(int));
1554 dummy = read(help_file, (char *)&num_topic, sizeof(int));
1555 dummy = read(help_file, (char *)&num_label, sizeof(int));
1556 help_seek((long)6*sizeof(int)); /* skip num_contents and num_doc_pages */
1557
1558 assert(max_pages > 0);
1559 assert(max_links >= 0);
1560 assert(num_topic > 0);
1561 assert(num_label > 0);
1562
1563 /* allocate one big chunk for all three arrays */
1564
1565 topic_offset = (long far *)farmemalloc(sizeof(long)*num_topic + 2L*sizeof(int)*num_label + sizeof(HIST)*MAX_HIST);
1566
1567 if (topic_offset == NULL)
1568 {
1569 static FCODE err_no_mem[] = "Not enough memory for help system!\n";
1570 close(help_file);
1571 help_file = -1;
1572 stopmsg(1, err_no_mem);
1573
1574 return (-2);
1575 }
1576
1577 /* split off the other arrays */
1578
1579 label = (LABEL far *)(&topic_offset[num_topic]);
1580 hist = (HIST far *)(&label[num_label]);
1581
1582 /* read in the tables... */
1583
1584 farread(help_file, topic_offset, num_topic*sizeof(long));
1585 farread(help_file, label, num_label*2*sizeof(int));
1586
1587 /* finished! */
1588
1589 return (0); /* success */
1590 }
1591
end_help(void)1592 void end_help(void)
1593 {
1594 if (help_file != -1)
1595 {
1596 close(help_file);
1597 farmemfree((BYTE far *)topic_offset);
1598 help_file = -1;
1599 }
1600 }
1601