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