File: common\help.c
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
106 static void help_seek(long pos)
107 {
108 lseek(help_file, base_off+pos, SEEK_SET);
109 }
110
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)
129 color = (signed int)INVERSE;
130 else if (color & BRIGHT)
131 {
132 color = 0; /* normal */
133 if (ch>='a' && ch<='z')
134 ch += 'A' - 'a';
135 }
136 else
137 color = 0; /* normal */
138 }
139
140 s[0] = (char)ch;
141 putstring(row, col, color, s);
142 }
143
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
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
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
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");
350 #endif
351 PUT_KEY("Enter", "Go to");
352 PUT_KEY("Backspace", "Last topic");
353 PUT_KEY("Escape", "Exit help");
354 }
355
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
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);
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
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
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
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
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__
555 #endif
556
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
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
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
609 where = topic_offset[curr->topic_num]+sizeof(int); /* to skip flags */
610 curr_link = curr->link;
611
612 help_seek(where);
613
614 read(help_file, (char *)&num_pages, sizeof(int));
615 assert(num_pages>0 && num_pages<=max_pages);
616
617 farread(help_file, (char far *)page_table, 3*sizeof(int)*num_pages);
618
619 read(help_file, &ch, 1);
620 len = ch;
621 assert(len<81);
622 read(help_file, (char *)title, len);
623 title[len] = '\0';
624
625 where += sizeof(int) + num_pages*3*sizeof(int) + 1 + len + sizeof(int);
626
627 for(page=0; page<num_pages; page++)
628 if (curr->topic_off >= page_table[page].offset &&
629 curr->topic_off < page_table[page].offset+page_table[page].len )
630 break;
631
632 assert(page < num_pages);
633
634 action = -1;
635 draw_page = 2;
636
637 do
638 {
639 if (draw_page)
640 {
641 help_seek(where+page_table[page].offset);
642 farread(help_file, buffer, page_table[page].len);
643
644 num_link = 0;
645 display_page(title, buffer, page_table[page].len, page, num_pages,
646 page_table[page].margin, &num_link, link_table);
647
648 if (draw_page==2)
649 {
650 assert(num_link<=0 || (curr_link>=0 && curr_link<num_link));
651 }
652 else if (draw_page==3)
653 curr_link = num_link - 1;
654 else
655 curr_link = 0;
656
657 if (num_link > 0)
658 color_link(&link_table[curr_link], C_HELP_CURLINK);
659
660 draw_page = 0;
661 }
662
663 key = getakey();
664
665 switch(key)
666 {
667 case PAGE_DOWN:
668 if (page<num_pages-1)
669 {
670 page++;
671 draw_page = 1;
672 }
673 break;
674
675 case PAGE_UP:
676 if (page>0)
677 {
678 page--;
679 draw_page = 1;
680 }
681 break;
682
683 case HOME:
684 if ( page != 0 )
685 {
686 page = 0;
687 draw_page = 1;
688 }
689 else
690 do_move_link(link_table, num_link, &curr_link, NULL, 0);
691 break;
692
693 case END:
694 if ( page != num_pages-1 )
695 {
696 page = num_pages-1;
697 draw_page = 3;
698 }
699 else
700 do_move_link(link_table, num_link, &curr_link, NULL, num_link-1);
701 break;
702
703 case TAB:
704 if ( !do_move_link(link_table, num_link, &curr_link, find_link_key, key) &&
705 page<num_pages-1 )
706 {
707 ++page;
708 draw_page = 1;
709 }
710 break;
711
712 case BACK_TAB:
713 if ( !do_move_link(link_table, num_link, &curr_link, find_link_key, key) &&
714 page>0 )
715 {
716 --page;
717 draw_page = 3;
718 }
719 break;
720
721 case DOWN_ARROW:
722 if ( !do_move_link(link_table, num_link, &curr_link, find_link_updown, 0) &&
723 page<num_pages-1 )
724 {
725 ++page;
726 draw_page = 1;
727 }
728 break;
729
730 case UP_ARROW:
731 if ( !do_move_link(link_table, num_link, &curr_link, find_link_updown, 1) &&
732 page>0 )
733 {
734 --page;
735 draw_page = 3;
736 }
737 break;
738
739 case LEFT_ARROW:
740 do_move_link(link_table, num_link, &curr_link, find_link_leftright, 1);
741 break;
742
743 case RIGHT_ARROW:
744 do_move_link(link_table, num_link, &curr_link, find_link_leftright, 0);
745 break;
746
747 case ESC: /* exit help */
748 action = ACTION_QUIT;
749 break;
750
751 case BACKSPACE: /* prev topic */
752 case ALT_F1:
753 if (flags & F_HIST)
754 action = ACTION_PREV;
755 break;
756
757 case F1: /* help index */
758 if (!(flags & F_INDEX))
759 action = ACTION_INDEX;
760 break;
761
762 case ENTER:
763 case ENTER_2:
764 if (num_link > 0)
765 {
766 next->topic_num = link_table[curr_link].topic_num;
767 next->topic_off = link_table[curr_link].topic_off;
768 action = ACTION_CALL;
769 }
770 break;
771 } /* switch */
772 }
773 while ( action == -1 );
774
775 curr->topic_off = page_table[page].offset;
776 curr->link = curr_link;
777
778 return (action);
779 }
780
781 int help(int action)
782 {
783 static FCODE unknowntopic_msg[] = "Unknown Help Topic";
784 HIST curr;
785 int oldlookatmouse;
786 int oldhelpmode;
787 int flags;
788 HIST next;
789
790 if (helpmode == -1) /* is help disabled? */
791 {
792 return (0);
793 }
794
795 if (help_file == -1)
796 {
797 buzzer(2);
798 return (0);
799 }
800
801 buffer = (char far *)farmemalloc((long)MAX_PAGE_SIZE + sizeof(LINK)*max_links +
802 sizeof(PAGE)*max_pages);
803
804 if (buffer == NULL)
805 {
806 buzzer(2);
807 return (0);
808 }
809
810 link_table = (LINK far *)(&buffer[MAX_PAGE_SIZE]);
811 page_table = (PAGE far *)(&link_table[max_links]);
812
813 oldlookatmouse = lookatmouse;
814 lookatmouse = 0;
815 timer_start -= clock_ticks();
816 stackscreen();
817
818 if (helpmode >= 0)
819 {
820 next.topic_num = label[helpmode].topic_num;
821 next.topic_off = label[helpmode].topic_off;
822 }
823 else
824 {
825 next.topic_num = helpmode;
826 next.topic_off = 0;
827 }
828
829 oldhelpmode = helpmode;
830
831 if (curr_hist <= 0)
832 action = ACTION_CALL; /* make sure it isn't ACTION_PREV! */
833
834 do
835 {
836 switch(action)
837 {
838 case ACTION_PREV2:
839 if (curr_hist > 0)
840 curr = hist[--curr_hist];
841
842 /* fall-through */
843
844 case ACTION_PREV:
845 if (curr_hist > 0)
846 curr = hist[--curr_hist];
847 break;
848
849 case ACTION_QUIT:
850 break;
851
852 case ACTION_INDEX:
853 next.topic_num = label[HELP_INDEX].topic_num;
854 next.topic_off = label[HELP_INDEX].topic_off;
855
856 /* fall-through */
857
858 case ACTION_CALL:
859 curr = next;
860 curr.link = 0;
861 break;
862 } /* switch */
863
864 flags = 0;
865 if (curr.topic_num == label[HELP_INDEX].topic_num)
866 flags |= F_INDEX;
867 if (curr_hist > 0)
868 flags |= F_HIST;
869
870 if ( curr.topic_num >= 0 )
871 action = help_topic(&curr, &next, flags);
872 else
873 {
874 if ( curr.topic_num == -100 )
875 {
876 print_document("FRACTINT.DOC", print_doc_msg_func, 1);
877 action = ACTION_PREV2;
878 }
879
880 else if ( curr.topic_num == -101 )
881 action = ACTION_PREV2;
882
883 else
884 {
885 display_page(unknowntopic_msg, NULL, 0, 0, 1, 0, NULL, NULL);
886 action = -1;
887 while (action == -1)
888 {
889 switch (getakey())
890 {
891 case ESC: action = ACTION_QUIT; break;
892 case ALT_F1: action = ACTION_PREV; break;
893 case F1: action = ACTION_INDEX; break;
894 } /* switch */
895 } /* while */
896 }
897 } /* else */
898
899 if ( action != ACTION_PREV && action != ACTION_PREV2 )
900 {
901 if (curr_hist >= MAX_HIST)
902 {
903 int ctr;
904
905 for (ctr=0; ctr<MAX_HIST-1; ctr++)
906 hist[ctr] = hist[ctr+1];
907
908 curr_hist = MAX_HIST-1;
909 }
910 hist[curr_hist++] = curr;
911 }
912 }
913 while (action != ACTION_QUIT);
914
915 farmemfree((BYTE far *)buffer);
916
917 unstackscreen();
918 lookatmouse = oldlookatmouse;
919 helpmode = oldhelpmode;
920 timer_start += clock_ticks();
921
922 return(0);
923 }
924
925 #ifndef XFRACT
926 static int dos_version(void)
927 {
928 union REGS r;
929
930 r.x.ax = 0x3000;
931 intdos(&r, &r);
932
933 return (r.h.al*100 + r.h.ah);
934 }
935
936 static char s_fractintexe[] = "FRACTINT.EXE";
937 #endif
938
939 static int can_read_file(char *path)
940 {
941 int handle;
942
943 #ifdef __TURBOC__
945 #else
946 if ( (handle=open(path, O_RDONLY)) != -1)
947 #endif
948 {
949 close(handle);
950 return (1);
951 }
952 else
953 return (0);
954 }
955
956
957 static int exe_path(char *filename, char *path)
958 {
959 #ifndef XFRACT
960 char *ptr;
961
962 if (dos_version() >= 300) /* DOS version 3.00+ ? */
963 {
964 #ifdef __TURBOC__
966 #else /* assume MSC */
967 extern char **__argv;
968 strcpy(path, __argv[0]); /* note: __argv may be undocumented in MSC */
969 #endif
970 if(strcmp(filename,s_fractintexe)==0)
971 if (can_read_file(path))
972 return (1);
973 ptr = strrchr(path, SLASHC);
974 if (ptr == NULL)
975 ptr = path;
976 else
977 ++ptr;
978 strcpy(ptr, filename);
979 return (1);
980 }
981
982 return (0);
988 #endif
989 }
990
991 static int find_file(char *filename, char *path)
992 {
993 if ( exe_path(filename, path) )
994 if( can_read_file(path))
995 return (1);
996 findpath(filename,path);
997 return ( (path[0]) ? 1 : 0);
998 }
999
1000 static int _read_help_topic(int topic, int off, int len, VOIDFARPTR buf)
1001 {
1002 static int curr_topic = -1;
1003 static long curr_base;
1004 static int curr_len;
1005 int read_len;
1006
1007 if ( topic != curr_topic )
1008 {
1009 int t;
1010 char ch;
1011
1012 curr_topic = topic;
1013
1014 curr_base = topic_offset[topic];
1015
1016 curr_base += sizeof(int); /* skip flags */
1017
1018 help_seek(curr_base);
1019 read(help_file, (char *)&t, sizeof(int)); /* read num_pages */
1020 curr_base += sizeof(int) + t*3*sizeof(int); /* skip page info */
1021
1022 if (t>0)
1023 help_seek(curr_base);
1024 read(help_file, &ch, 1); /* read title_len */
1025 t = ch;
1026 curr_base += 1 + t; /* skip title */
1027
1028 if (t>0)
1029 help_seek(curr_base);
1030 read(help_file, (char *)&curr_len, sizeof(int)); /* read topic len */
1031 curr_base += sizeof(int);
1032 }
1033
1034 read_len = (off+len > curr_len) ? curr_len - off : len;
1035
1036 if (read_len > 0)
1037 {
1038 help_seek(curr_base + off);
1039 farread(help_file, (char far *)buf, read_len);
1040 }
1041
1042 return ( curr_len - (off+len) );
1043 }
1044
1045 int read_help_topic(int label_num, int off, int len, VOIDFARPTR buf)
1046 /*
1047 * reads text from a help topic. Returns number of bytes from (off+len)
1048 * to end of topic. On "EOF" returns a negative number representing
1049 * number of bytes not read.
1050 */
1051 {
1052 int ret;
1053 ret = _read_help_topic(label[label_num].topic_num,
1054 label[label_num].topic_off + off, len, buf);
1055 return ( ret );
1056 }
1057
1058 #define PRINT_BUFFER_SIZE (32767) /* max. size of help topic in doc. */
1059 #define TEMP_FILE_NAME "HELP.$$$" /* temp file for storing extraseg */
1060 /* while printing document */
1061 #define MAX_NUM_TOPIC_SEC (10) /* max. number of topics under any */
1062 /* single section (CONTENT) */
1063
1064 typedef struct PRINT_DOC_INFO
1065 {
1066 int cnum; /* current CONTENT num */
1067 int tnum; /* current topic num */
1068
1069 long content_pos; /* current CONTENT item offset in file */
1070 int num_page; /* total number of pages in document */
1071
1072 int num_contents, /* total number of CONTENT entries */
1073 num_topic; /* number of topics in current CONTENT */
1074
1075 int topic_num[MAX_NUM_TOPIC_SEC]; /* topic_num[] for current CONTENT entry */
1076
1077 char far *buffer; /* text buffer */
1078
1079 char id[81]; /* buffer to store id in */
1080 char title[81]; /* buffer to store title in */
1081
1082 #ifndef XFRACT
1083 int (*msg_func)(int pnum, int num_page);
1087 #endif
1088
1089 FILE *file; /* file to sent output to */
1090 int margin; /* indent text by this much */
1091 int start_of_line; /* are we at the beginning of a line? */
1092 int spaces; /* number of spaces in a row */
1093 } PRINT_DOC_INFO;
1094
1095 void print_document(char *outfname, int (*msg_func)(int,int), int save_extraseg );
1096
1097 static void printerc(PRINT_DOC_INFO *info, int c, int n)
1098 {
1099 while ( n-- > 0 )
1100 {
1101 if (c==' ')
1102 ++info->spaces;
1103
1104 else if (c=='\n' || c=='\f')
1105 {
1106 info->start_of_line = 1;
1107 info->spaces = 0; /* strip spaces before a new-line */
1108 putc(c, info->file);
1109 }
1110
1111 else
1112 {
1113 if (info->start_of_line)
1114 {
1115 info->spaces += info->margin;
1116 info->start_of_line = 0;
1117 }
1118
1119 while (info->spaces > 0)
1120 {
1121 fputc(' ', info->file);
1122 --info->spaces;
1123 }
1124
1125 fputc(c, info->file);
1126 }
1127 }
1128 }
1129
1130 static void printers(PRINT_DOC_INFO *info, char far *s, int n)
1131 {
1132 if (n > 0)
1133 {
1134 while ( n-- > 0 )
1135 printerc(info, *s++, 1);
1136 }
1137 else
1138 {
1139 while ( *s != '\0' )
1140 printerc(info, *s++, 1);
1141 }
1142 }
1143
1144 static int print_doc_get_info(int cmd, PD_INFO *pd, PRINT_DOC_INFO *info)
1145 {
1146 int t;
1147 BYTE ch;
1148
1149 switch (cmd)
1150 {
1151 case PD_GET_CONTENT:
1152 if ( ++info->cnum >= info->num_contents )
1153 return (0);
1154
1155 help_seek( info->content_pos );
1156
1157 read(help_file, (char *)&t, sizeof(int)); /* read flags */
1158 info->content_pos += sizeof(int);
1159 pd->new_page = (t & 1) ? 1 : 0;
1160
1161 read(help_file, &ch, 1); /* read id len */
1162 t = ch;
1163 assert(t<80);
1164 read(help_file, (char *)info->id, t); /* read the id */
1165 info->content_pos += 1 + t;
1166 info->id[t] = '\0';
1167
1168 read(help_file, (char *)&ch, 1); /* read title len */
1169 t = ch;
1170 assert(t<80);
1171 read(help_file, (char *)info->title, t); /* read the title */
1172 info->content_pos += 1 + t;
1173 info->title[t] = '\0';
1174
1175 read(help_file, (char *)&ch, 1); /* read num_topic */
1176 t = ch;
1177 assert(t<MAX_NUM_TOPIC_SEC);
1178 read(help_file, (char *)info->topic_num, t*sizeof(int)); /* read topic_num[] */
1179 info->num_topic = t;
1180 info->content_pos += 1 + t*sizeof(int);
1181
1182 info->tnum = -1;
1183
1184 pd->id = info->id;
1185 pd->title = info->title;
1186 return (1);
1187
1188 case PD_GET_TOPIC:
1189 if ( ++info->tnum >= info->num_topic )
1190 return (0);
1191
1192 t = _read_help_topic(info->topic_num[info->tnum], 0, PRINT_BUFFER_SIZE, info->buffer);
1193
1194 assert(t <= 0);
1195
1196 pd->curr = info->buffer;
1197 pd->len = PRINT_BUFFER_SIZE + t; /* same as ...SIZE - abs(t) */
1198 return (1);
1199
1200 case PD_GET_LINK_PAGE:
1201 pd->i = getint(pd->s+sizeof(long));
1202 return ( (pd->i == -1) ? 0 : 1 );
1203
1204 case PD_RELEASE_TOPIC:
1205 return (1);
1206
1207 default:
1208 return (0);
1209 }
1210 }
1211
1212 static int print_doc_output(int cmd, PD_INFO *pd, PRINT_DOC_INFO *info)
1213 {
1214 switch (cmd)
1215 {
1216 case PD_HEADING:
1217 {
1218 char line[81];
1219 char buff[40];
1220 int width = PAGE_WIDTH + PAGE_INDENT;
1221 int keep_going;
1222
1223 if ( info->msg_func != NULL )
1224 keep_going = (*info->msg_func)(pd->pnum, info->num_page);
1225 else
1226 keep_going = 1;
1227
1228 info->margin = 0;
1229
1230 memset(line, ' ', 81);
1231 sprintf(buff, "Fractint Version %d.%01d%c",release/100, (release%100)/10,
1232 ( (release%10) ? '0'+(release%10) : ' ') );
1233 memmove(line + ((width-(int)(strlen(buff))) / 2)-4, buff, strlen(buff));
1234
1235 sprintf(buff, "Page %d", pd->pnum);
1236 memmove(line + (width - (int)strlen(buff)), buff, strlen(buff));
1237
1238 printerc(info, '\n', 1);
1239 printers(info, line, width);
1240 printerc(info, '\n', 2);
1241
1242 info->margin = PAGE_INDENT;
1243
1244 return ( keep_going );
1245 }
1246
1247 case PD_FOOTING:
1248 info->margin = 0;
1249 printerc(info, '\f', 1);
1250 info->margin = PAGE_INDENT;
1251 return (1);
1252
1253 case PD_PRINT:
1254 printers(info, pd->s, pd->i);
1255 return (1);
1256
1257 case PD_PRINTN:
1258 printerc(info, *pd->s, pd->i);
1259 return (1);
1260
1261 case PD_PRINT_SEC:
1262 info->margin = TITLE_INDENT;
1263 if (pd->id[0] != '\0')
1264 {
1265 printers(info, pd->id, 0);
1266 printerc(info, ' ', 1);
1267 }
1268 printers(info, pd->title, 0);
1269 printerc(info, '\n', 1);
1270 info->margin = PAGE_INDENT;
1271 return (1);
1272
1273 case PD_START_SECTION:
1274 case PD_START_TOPIC:
1275 case PD_SET_SECTION_PAGE:
1276 case PD_SET_TOPIC_PAGE:
1277 case PD_PERIODIC:
1278 return (1);
1279
1280 default:
1281 return (0);
1282 }
1283 }
1284
1285 static int print_doc_msg_func(int pnum, int num_pages)
1286 {
1287 char temp[10];
1288 int key;
1289
1290 if ( pnum == -1 ) /* successful completion */
1291 {
1292 static FCODE msg[] = {"Done -- Press any key"};
1293 buzzer(0);
1294 putstringcenter(7, 0, 80, C_HELP_LINK, msg);
1295 getakey();
1296 return (0);
1297 }
1298
1299 if ( pnum == -2 ) /* aborted */
1300 {
1301 static FCODE msg[] = {"Aborted -- Press any key"};
1302 buzzer(1);
1303 putstringcenter(7, 0, 80, C_HELP_LINK, msg);
1304 getakey();
1305 return (0);
1306 }
1307
1308 if (pnum == 0) /* initialization */
1309 {
1310 static FCODE msg[] = {"Generating FRACTINT.DOC"};
1311 helptitle();
1312 printinstr();
1313 setattr(2, 0, C_HELP_BODY, 80*22);
1314 putstringcenter(1, 0, 80, C_HELP_HDG, msg);
1315
1316 putstring(7, 30, C_HELP_BODY, "Completed:");
1317
1318 movecursor(25,80); /* hide cursor */
1319 }
1320
1321 sprintf(temp, "%d%%", (int)( (100.0 / num_pages) * pnum ) );
1322 putstring(7, 41, C_HELP_LINK, temp);
1323
1324 while ( keypressed() )
1325 {
1326 key = getakey();
1327 if ( key == ESC )
1328 return (0); /* user abort */
1329 }
1330
1331 return (1); /* AOK -- continue */
1332 }
1333
1334 int makedoc_msg_func(int pnum, int num_pages)
1335 {
1336 if (pnum >= 0)
1337 {
1338 printf("\rcompleted %d%%", (int)( (100.0 / num_pages) * pnum ) );
1339 return (1);
1340 }
1341 if ( pnum == -2 )
1342 printf("\n*** aborted");
1343 printf("\n");
1344 return (0);
1345 }
1346
1347 void print_document(char *outfname, int (*msg_func)(int,int), int save_extraseg )
1348 {
1349 static FCODE err_no_temp[] = "Unable to create temporary file.\n";
1350 static FCODE err_no_out[] = "Unable to create output file.\n";
1351 static FCODE err_badwrite[] = "Error writing temporary file.\n";
1352 static FCODE err_badread[] = "Error reading temporary file.\nSystem may be corrupt!\nSave your image and re-start FRACTINT!\n";
1353
1354 PRINT_DOC_INFO info;
1355 int success = 0;
1356 int temp_file = -1;
1357 char far *msg = NULL;
1358
1359 info.buffer = MK_FP(extraseg, 0);
1360
1361 /* help_seek((long)sizeof(int)+sizeof(long)); Strange -- should be 8 -- CWM */
1362 help_seek(8L); /* indeed it should - Bert */
1363 read(help_file, (char *)&info.num_contents, sizeof(int));
1364 read(help_file, (char *)&info.num_page, sizeof(int));
1365
1366 info.cnum = info.tnum = -1;
1367 info.content_pos = sizeof(long)+4*sizeof(int) + num_topic*sizeof(long) + num_label*2*sizeof(int);
1368 info.msg_func = msg_func;
1369
1370 if ( msg_func != NULL )
1371 msg_func(0, info.num_page); /* initialize */
1372
1373 if ( save_extraseg )
1374 {
1375 if ( (temp_file=open(TEMP_FILE_NAME, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, S_IREAD|S_IWRITE)) == -1 )
1376 {
1377 msg = err_no_temp;
1378 goto ErrorAbort;
1379 }
1380
1381 if ( farwrite(temp_file, info.buffer, PRINT_BUFFER_SIZE) != PRINT_BUFFER_SIZE )
1382 {
1383 msg = err_badwrite;
1384 goto ErrorAbort;
1385 }
1386 }
1387
1388 if ( (info.file = fopen(outfname, "wt")) == NULL )
1389 {
1390 msg = err_no_out;
1391 goto ErrorAbort;
1392 }
1393
1394 info.margin = PAGE_INDENT;
1395 info.start_of_line = 1;
1396 info.spaces = 0;
1397
1398 success = process_document((PD_FUNC)print_doc_get_info,
1399 (PD_FUNC)print_doc_output, &info);
1400 fclose(info.file);
1401
1402 if ( save_extraseg )
1403 {
1404 if ( lseek(temp_file, 0L, SEEK_SET) != 0L )
1405 {
1406 msg = err_badread;
1407 goto ErrorAbort;
1408 }
1409
1410 if ( farread(temp_file, info.buffer, PRINT_BUFFER_SIZE) != PRINT_BUFFER_SIZE )
1411 {
1412 msg = err_badread;
1413 goto ErrorAbort;
1414 }
1415 }
1416
1417 ErrorAbort:
1418 if (temp_file != -1)
1419 {
1420 close(temp_file);
1421 remove(TEMP_FILE_NAME);
1422 temp_file = -1;
1423 }
1424
1425 if ( msg != NULL )
1426 {
1427 helptitle();
1428 stopmsg(1, msg);
1429 }
1430
1431 else if ( msg_func != NULL )
1432 msg_func((success) ? -1 : -2, info.num_page );
1433 }
1434
1435 int init_help(void)
1436 {
1437 struct help_sig_info hs;
1438 char path[FILE_MAX_PATH+1];
1439
1440 help_file = -1;
1441
1442 #ifndef WINFRACT
1443 #ifndef XFRACT
1444 if (help_file == -1) /* now look for help files in FRACTINT.EXE */
1445 {
1446 static FCODE err_no_open[] = "Help system was unable to open FRACTINT.EXE!\n";
1447 static FCODE err_no_exe[] = "Help system couldn't find FRACTINT.EXE!\n";
1448 static FCODE err_wrong_ver[] = "Wrong help version in FRACTINT.EXE!\n";
1449 /*
1450 static FCODE err_not_in_exe[] = "Help not found in FRACTINT.EXE!\n";
1451 */
1452
1453 if ( find_file(s_fractintexe, path) )
1454 {
1455 #ifdef __TURBOC__
1457 #else
1458 if ( (help_file = open(path, O_RDONLY|O_BINARY)) != -1 )
1459 #endif
1460 {
1461 long help_offset;
1462
1463 for (help_offset = -((long)sizeof(hs)); help_offset >= -128L; help_offset--)
1464 {
1465 lseek(help_file, help_offset, SEEK_END);
1466 read(help_file, (char *)&hs, sizeof(hs));
1467 if (hs.sig == HELP_SIG) break;
1468 }
1469
1470 if ( hs.sig != HELP_SIG )
1471 {
1472 close(help_file);
1473 help_file = -1;
1474 /* (leave out the error message)
1475 stopmsg(1, err_not_in_exe);
1476 */
1477 }
1478
1479 else
1480 {
1481 if ( hs.version != HELP_VERSION )
1482 {
1483 close(help_file);
1484 help_file = -1;
1485 stopmsg(1, err_wrong_ver);
1486 }
1487 else
1488 base_off = hs.base;
1489
1490 }
1491 }
1492 else
1493 stopmsg(1, err_no_open);
1494 }
1495 else
1496 stopmsg(1, err_no_exe);
1497
1498 }
1499 #endif
1500 #endif
1501
1502 if (help_file == -1) /* look for FRACTINT.HLP */
1503 {
1504 if ( find_file("fractint.hlp", path) )
1505 {
1506 #ifdef __TURBOC__
1508 #else
1509 if ( (help_file = open(path, O_RDONLY|O_BINARY)) != -1 )
1510 #endif
1511 {
1512 read(help_file, (char *)&hs, sizeof(long)+sizeof(int));
1513
1514 if ( hs.sig != HELP_SIG )
1515 {
1516 static FCODE msg[] = {"Invalid help signature in FRACTINT.HLP!\n"};
1517 close(help_file);
1518 stopmsg(1, msg);
1519 }
1520
1521 else if ( hs.version != HELP_VERSION )
1522 {
1523 static FCODE msg[] = {"Wrong help version in FRACTINT.HLP!\n"};
1524 close(help_file);
1525 stopmsg(1, msg);
1526 }
1527
1528 else
1529 base_off = sizeof(long)+sizeof(int);
1530 }
1531 }
1532 }
1533
1534 if (help_file == -1) /* Can't find the help files anywhere! */
1535 {
1536 static FCODE msg[] =
1537 #ifndef XFRACT
1538 {"Help Files aren't in FRACTINT.EXE, and couldn't find FRACTINT.HLP!\n"};
1539 #else
1540 {"Couldn't find fractint.hlp; set FRACTDIR to proper directory with setenv.\n"}; 1541 #endif
1542 stopmsg(1, msg);
1543 }
1544
1545 help_seek(0L);
1546
1547 read(help_file, (char *)&max_pages, sizeof(int));
1548 read(help_file, (char *)&max_links, sizeof(int));
1549 read(help_file, (char *)&num_topic, sizeof(int));
1550 read(help_file, (char *)&num_label, sizeof(int));
1551 help_seek((long)6*sizeof(int)); /* skip num_contents and num_doc_pages */
1552
1553 assert(max_pages > 0);
1554 assert(max_links >= 0);
1555 assert(num_topic > 0);
1556 assert(num_label > 0);
1557
1558 /* allocate one big chunk for all three arrays */
1559
1560 topic_offset = (long far *)farmemalloc(sizeof(long)*num_topic + 2L*sizeof(int)*num_label + sizeof(HIST)*MAX_HIST);
1561
1562 if (topic_offset == NULL)
1563 {
1564 static FCODE err_no_mem[] = "Not enough memory for help system!\n";
1565 close(help_file);
1566 help_file = -1;
1567 stopmsg(1, err_no_mem);
1568
1569 return (-2);
1570 }
1571
1572 /* split off the other arrays */
1573
1574 label = (LABEL far *)(&topic_offset[num_topic]);
1575 hist = (HIST far *)(&label[num_label]);
1576
1577 /* read in the tables... */
1578
1579 farread(help_file, topic_offset, num_topic*sizeof(long));
1580 farread(help_file, label, num_label*2*sizeof(int));
1581
1582 /* finished! */
1583
1584 return (0); /* success */
1585 }
1586
1587 void end_help(void)
1588 {
1589 if (help_file != -1)
1590 {
1591 close(help_file);
1592 farmemfree((BYTE far *)topic_offset);
1593 help_file = -1;
1594 }
1595 }
1596