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");
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 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);
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 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__
554 # pragma argsused
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__
944 if ( (handle=open(path, O_RDONLY|O_DENYWRITE)) != -1)
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__
965 strcpy(path, _argv[0]);
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);
983 #else 984 strcpy(path,SRCDIR); 985 strcat(path,"/"); 986 strcat(path,filename); 987 return 1;
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);
1084 #else 1085 int (*msg_func)(); 1086 int pnum;
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__
1456 if ( (help_file = open(path, O_RDONLY|O_BINARY|O_DENYWRITE)) != -1 )
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__
1507 if ( (help_file = open(path, O_RDONLY|O_BINARY|O_DENYWRITE)) != -1 )
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