home *** CD-ROM | disk | FTP | other *** search
/ Mega A/V / mega_av.zip / mega_av / GRAPHUTL / FRPOR172.ZIP / HC.C < prev    next >
C/C++ Source or Header  |  1992-03-02  |  87KB  |  3,915 lines

  1.  
  2. /*
  3.  * hc.c
  4.  *
  5.  * Stand-alone FRACTINT help compiler.  Compile in the COMPACT memory model.
  6.  *
  7.  * See HC.DOC for source file syntax.
  8.  *
  9.  *
  10.  * Revision History:
  11.  *
  12.  *   02-26-91 EAN     Initial version.
  13.  *
  14.  *   03-21-91 EAN     Modified for automatic paragraph formatting.
  15.  *              Added several new commands:
  16.  *             Format[+/-]  Enable/disable paragraph formatting
  17.  *             Doc[+/-]     Enable/disable output to document.
  18.  *             Online[+/-]  Enable/disable output to online help.
  19.  *             Label=       Defines a label. Replaces ~(...)
  20.  *             FF          Forces a form-feed.  Replaces ~~
  21.  *             FormatExclude=val Exclude lines past val from
  22.  *                      formatting.  If before any topic sets
  23.  *                      global default, otherwise set local.
  24.  *             FormatExclude= Set to global default.
  25.  *             FormatExclude=n Disable exclusion. (global or local)
  26.  *             FormatExclude[+/-] Enable/disable format exclusion.
  27.  *             Center[+/-]  Enable/disable centering of text.
  28.  *             \ before nl  Forces the end of a paragraph
  29.  *              Support for commands embedded in text with new
  30.  *              ~(...) format.
  31.  *              Support for multiple commands on a line separated by
  32.  *              commas.
  33.  *              Support for implict links; explicit links must now
  34.  *              start with an equal sign.
  35.  *   04-03-91 EAN     Added "include" command (works like #include)
  36.  *   04-10-91 EAN     Added support for "data" topics.
  37.  *              Added Comment/EndComment commands for multi-line
  38.  *             comments.
  39.  *              Added CompressSpaces[+/-] command.
  40.  *              Added DocContents command for document printing.
  41.  *              Added BinInc command which includes a binary file
  42.  *             in a data topic.
  43.  *              Fixed tables to flow down instead of across the page.
  44.  *             Makes no allowances for page breaks within tables.
  45.  *
  46.  */
  47.  
  48.  
  49. #define HC_C
  50.  
  51. #define INCLUDE_COMMON  /* tell helpcom.h to include common code */
  52.  
  53.  
  54. #include <stdio.h>
  55. #ifndef XFRACT
  56. #include <io.h>
  57. #include <stdarg.h>
  58. #else
  59. #include <varargs.h>
  60. #define strupr strlwr
  61. #endif
  62. #include <fcntl.h>
  63. #include <stdlib.h>
  64. #include <string.h>
  65. #include <ctype.h>
  66.  
  67. #ifdef __TURBOC__
  68. #   include <dir.h>
  69. #   define FNSPLIT fnsplit
  70. #else
  71. #   define MAXFILE _MAX_FNAME
  72. #   define MAXEXT  _MAX_EXT
  73. #   define FNSPLIT _splitpath
  74. #endif
  75.  
  76.  
  77. #include <assert.h>
  78. #include "helpcom.h"
  79.  
  80.  
  81. /*
  82.  * When defined, SHOW_ERROR_LINE will cause the line number in HC.C where
  83.  * errors/warnings/messages are generated to be displayed at the start of
  84.  * the line.
  85.  *
  86.  * Used when debugging HC.  Also useful for finding the line (in HC.C) that
  87.  * generated a error or warning.
  88.  */
  89.  
  90. #ifndef XFRACT
  91. #define SHOW_ERROR_LINE
  92. #endif
  93.  
  94.  
  95. #define DEFAULT_SRC_FNAME "help.src"
  96. #define DEFAULT_HLP_FNAME "fractint.hlp"
  97. #define DEFAULT_EXE_FNAME "fractint.exe"
  98. #define DEFAULT_DOC_FNAME "fractint.doc"
  99.  
  100. #define TEMP_FNAME      "HC.$$$"
  101. #define SWAP_FNAME      "HCSWAP.$$$"
  102.  
  103. #define MAX_ERRORS      (25)     /* stop after this many errors */
  104. #define MAX_WARNINGS      (25)     /* stop after this many warnings */
  105.                  /* 0 = never stop */
  106.  
  107. #define INDEX_LABEL      "HELP_INDEX"
  108. #define DOCCONTENTS_TITLE "DocContent"
  109.  
  110.  
  111.  
  112. #define BUFFER_SIZE   (24*1024)
  113.  
  114.  
  115. typedef struct
  116.    {
  117.    int        type;         /* 0 = name is topic title, 1 = name is label, */
  118.                  /*   2 = "special topic"; name is NULL and */
  119.                  /*   topic_num/topic_off is valid */
  120.    int        topic_num;         /* topic number to link to */
  121.    unsigned topic_off;         /* offset into topic to link to */
  122.    int        doc_page;         /* document page # to link to */
  123.    char    *name;         /* name of label or title of topic to link to */
  124.    char    *srcfile;         /* .SRC file link appears in */
  125.    int        srcline;         /* .SRC file line # link appears in */
  126.    } LINK;
  127.  
  128.  
  129. typedef struct
  130.    {
  131.    unsigned offset;     /* offset from start of topic text */
  132.    unsigned length;     /* length of page (in chars) */
  133.    int        margin;     /* if > 0 then page starts in_para and text */
  134.                 /* should be indented by this much */
  135.    } PAGE;
  136.  
  137.  
  138. /* values for TOPIC.flags */
  139.  
  140. #define TF_IN_DOC  (1)         /* 1 if topic is part of the printed document */
  141. #define TF_DATA    (2)         /* 1 if it is a "data" topic */
  142.  
  143.  
  144. typedef struct
  145.    {
  146.    unsigned  flags;         /* see #defines for TF_??? */
  147.    int         doc_page;         /* page number in document where topic starts */
  148.    unsigned  title_len;      /* length of title */
  149.    char     *title;         /* title for this topic */
  150.    int         num_page;         /* number of pages */
  151.    PAGE     *page;         /* list of pages */
  152.    unsigned  text_len;         /* lenth of topic text */
  153.    long      text;         /* topic text (all pages) */
  154.    long      offset;         /* offset to topic from start of file */
  155.    } TOPIC;
  156.  
  157.  
  158. typedef struct
  159.    {
  160.    char    *name;         /* its name */
  161.    int        topic_num;         /* topic number */
  162.    unsigned topic_off;         /* offset of label in the topic's text */
  163.    int        doc_page;
  164.    } LABEL;
  165.  
  166.  
  167. /* values for CONTENT.flags */
  168.  
  169. #define CF_NEW_PAGE  (1)     /* true if section starts on a new page */
  170.  
  171.  
  172. #define MAX_CONTENT_TOPIC (10)
  173.  
  174.  
  175. typedef struct
  176.    {
  177.    unsigned  flags;
  178.    char     *id;
  179.    char     *name;
  180.    int         doc_page;
  181.    unsigned  page_num_pos;
  182.    int         num_topic;
  183.    char      is_label[MAX_CONTENT_TOPIC];
  184.    char     *topic_name[MAX_CONTENT_TOPIC];
  185.    int         topic_num[MAX_CONTENT_TOPIC];
  186.    char     *srcfile;
  187.    int         srcline;
  188.    } CONTENT;
  189.  
  190.  
  191. struct help_sig_info
  192.    {
  193.    unsigned long sig;
  194.    int         version;
  195.    unsigned long base;
  196.    } ;
  197.  
  198.  
  199. int     num_topic      = 0;      /* topics */
  200. TOPIC   *topic;
  201.  
  202. int     num_label      = 0;      /* labels */
  203. LABEL   *label;
  204.  
  205. int     num_plabel      = 0;      /* private labels */
  206. LABEL   *plabel;
  207.  
  208. int     num_link      = 0;      /* all links */
  209. LINK    *a_link          = 0;
  210.  
  211. int     num_contents      = 0;      /* the table-of-contents */
  212. CONTENT *contents;
  213.  
  214. int     quiet_mode      = 0;      /* true if "/Q" option used */
  215.  
  216. int     max_pages      = 0;      /* max. pages in any topic */
  217. int     max_links      = 0;      /* max. links on any page */
  218. int     num_doc_pages      = 0;      /* total number of pages in document */
  219.  
  220. FILE    *srcfile;          /* .SRC file */
  221. int     srcline      = 0;      /* .SRC line number (used for errors) */
  222. int     srccol       = 0;      /* .SRC column. */
  223.  
  224. int     version      = -1;   /* help file version */
  225.  
  226. int     errors       = 0,      /* number of errors reported */
  227.      warnings      = 0;      /* number of warnings reported */
  228.  
  229. char     src_fname[81]      = "";   /* command-line .SRC filename */
  230. char     hdr_fname[81]      = "";   /* .H filename */
  231. char     hlp_fname[81]      = "";   /* .HLP filename */
  232. char    *src_cfname      = NULL; /* current .SRC filename */
  233.  
  234. int     format_exclude   = 0;      /* disable formatting at this col, 0 to */
  235.                   /*    never disable formatting */
  236. FILE    *swapfile;
  237. long     swappos;
  238.  
  239. char    *buffer;          /* alloc'ed as BUFFER_SIZE bytes */
  240. char    *curr;              /* current position in the buffer */
  241. char     cmd[128];          /* holds the current command */
  242. int     compress_spaces;
  243. int     xonline;
  244. int     xdoc;
  245.  
  246. #define  MAX_INCLUDE_STACK (5)      /* allow 5 nested includes */
  247.  
  248. struct
  249.    {
  250.    char *fname;
  251.    FILE *file;
  252.    int     line;
  253.    int     col;
  254.    } include_stack[MAX_INCLUDE_STACK];
  255. int include_stack_top = -1;
  256.  
  257.  
  258. #define CHK_BUFFER(off) { if ((unsigned)(curr+(off)) - (unsigned)buffer >= (BUFFER_SIZE-1024)) fatal(0,"Buffer overflowed -- Help topic too large."); }
  259.  
  260.  
  261. /*
  262.  * error/warning/message reporting functions.
  263.  */
  264.  
  265.  
  266. void report_errors(void)
  267.    {
  268.    printf("\n");
  269.    printf("Compiler Status:\n");
  270.    printf("%8d Error%c\n",     errors,   (errors==1)     ? ' ' : 's');
  271.    printf("%8d Warning%c\n",     warnings, (warnings==1) ? ' ' : 's');
  272.    }
  273.  
  274.  
  275. void print_msg(char *type, int lnum, char *format, va_list arg)
  276.    {
  277.    if (type != NULL)
  278.       {
  279.       printf("   %s", type);
  280.       if (lnum>0)
  281.      printf(" %s %d", src_cfname, lnum);
  282.       printf(": ");
  283.       }
  284.    vprintf(format, arg);
  285.    printf("\n");
  286.    }
  287.  
  288.  
  289. #ifndef XFRACT
  290. void fatal(int diff, char *format, ...)
  291. #else
  292. void fatal(va_alist)
  293.     va_dcl
  294. #endif
  295.    {
  296.    va_list arg;
  297.  
  298. #ifndef XFRACT
  299.    va_start(arg, format);
  300. #else
  301.    int diff;
  302.    char *format;
  303.    va_start(arg);
  304.    diff = va_arg(arg,int);
  305.    format = va_arg(arg,char *);
  306. #endif
  307.  
  308.    print_msg("Fatal", srcline-diff, format, arg);
  309.    va_end(arg);
  310.  
  311.    if ( errors || warnings )
  312.       report_errors();
  313.  
  314.    exit( errors + 1 );
  315.    }
  316.  
  317.  
  318. #ifndef XFRACT
  319. void error(int diff, char *format, ...)
  320. #else
  321. void error(va_alist)
  322.     va_dcl
  323. #endif
  324.    {
  325.    va_list arg;
  326.  
  327. #ifndef XFRACT
  328.    va_start(arg, format);
  329. #else
  330.    int diff;
  331.    char *format;
  332.    va_start(arg);
  333.    diff = va_arg(arg,int);
  334.    format = va_arg(arg,char *);
  335. #endif
  336.    print_msg("Error", srcline-diff, format, arg);
  337.    va_end(arg);
  338.  
  339.    if (++errors >= MAX_ERRORS && MAX_ERRORS > 0)
  340.       fatal(0,"Too many errors!");
  341.    }
  342.  
  343.  
  344. #ifndef XFRACT
  345. void warn(int diff, char *format, ...)
  346. #else
  347. void warn(va_alist)
  348.    va_dcl
  349. #endif
  350.    {
  351.    va_list arg;
  352. #ifndef XFRACT
  353.    va_start(arg, format);
  354. #else
  355.    int diff;
  356.    char *format;
  357.    va_start(arg);
  358.    diff = va_arg(arg, int);
  359.    format = va_arg(arg, char *);
  360. #endif
  361.    print_msg("Warning", srcline-diff, format, arg);
  362.    va_end(arg);
  363.  
  364.    if (++warnings >= MAX_WARNINGS && MAX_WARNINGS > 0)
  365.       fatal(0,"Too many warnings!");
  366.    }
  367.  
  368.  
  369. #ifndef XFRACT
  370. void notice(char *format, ...)
  371. #else
  372. void notice(va_alist)
  373.     va_dcl
  374. #endif
  375.    {
  376.    va_list arg;
  377. #ifndef XFRACT
  378.    va_start(arg, format);
  379. #else
  380.    char *format;
  381.  
  382.    va_start(arg);
  383.    format = va_arg(arg,char *);
  384. #endif
  385.    print_msg("Note", srcline, format, arg);
  386.    va_end(arg);
  387.    }
  388.  
  389.  
  390. #ifndef XFRACT
  391. void msg(char *format, ...)
  392. #else
  393. void msg(va_alist)
  394. va_dcl
  395. #endif
  396.    {
  397.    va_list arg;
  398. #ifdef XFRACT
  399.    char *format;
  400. #endif
  401.  
  402.    if (quiet_mode)
  403.       return;
  404. #ifndef XFRACT
  405.    va_start(arg, format);
  406. #else
  407.    va_start(arg);
  408.    format = va_arg(arg,char *);
  409. #endif
  410.    print_msg(NULL, 0, format, arg);
  411.    va_end(arg);
  412.    }
  413.  
  414.  
  415. #ifdef SHOW_ERROR_LINE
  416. #   define fatal  (printf("[%04d] ", __LINE__), fatal)
  417. #   define error  (printf("[%04d] ", __LINE__), error)
  418. #   define warn   (printf("[%04d] ", __LINE__), warn)
  419. #   define notice (printf("[%04d] ", __LINE__), notice)
  420. #   define msg      (printf((quiet_mode)?"":"[%04d] ", __LINE__), msg)
  421. #endif
  422.  
  423.  
  424. /*
  425.  * store-topic-text-to-disk stuff.
  426.  */
  427.  
  428.  
  429. void alloc_topic_text(TOPIC *t, unsigned size)
  430.    {
  431.    t->text_len = size;
  432.    t->text = swappos;
  433.    swappos += size;
  434.    fseek(swapfile, t->text, SEEK_SET);
  435.    fwrite(buffer, 1, t->text_len, swapfile);
  436.    }
  437.  
  438.  
  439. char *get_topic_text(TOPIC *t)
  440.    {
  441.    fseek(swapfile, t->text, SEEK_SET);
  442.    fread(buffer, 1, t->text_len, swapfile);
  443.    return (buffer);
  444.    }
  445.  
  446.  
  447. void release_topic_text(TOPIC *t, int save)
  448.    {
  449.    if ( save )
  450.       {
  451.       fseek(swapfile, t->text, SEEK_SET);
  452.       fwrite(buffer, 1, t->text_len, swapfile);
  453.       }
  454.    }
  455.  
  456.  
  457. /*
  458.  * memory-allocation functions.
  459.  */
  460.  
  461.  
  462. #define new(item)    (item *)newx(sizeof(item))
  463. #define delete(item) free(item)
  464.  
  465.  
  466. VOIDPTR newx(unsigned size)
  467.    {
  468.    VOIDPTR ptr;
  469.  
  470.    ptr = malloc(size);
  471.  
  472.    if (ptr == NULL)
  473.       fatal(0,"Out of memory!");
  474.  
  475.    return (ptr);
  476.    }
  477.  
  478.  
  479. VOIDPTR renewx(VOIDPTR ptr, unsigned size)
  480.    {
  481.    ptr = realloc(ptr, size);
  482.  
  483.    if (ptr == NULL)
  484.       fatal(0,"Out of memory!");
  485.  
  486.    return (ptr);
  487.    }
  488.  
  489.  
  490. char *dupstr(char *s, unsigned len)
  491.    {
  492.    char *ptr;
  493.  
  494.    if (len == 0)
  495.       len = strlen(s) + 1;
  496.  
  497.    ptr = newx(len);
  498.  
  499.    memcpy(ptr, s, len);
  500.  
  501.    return (ptr);
  502.    }
  503.  
  504.  
  505. #define LINK_ALLOC_SIZE (16)
  506.  
  507.  
  508. int add_link(LINK *l)
  509.    {
  510.    if (num_link == 0)
  511.       a_link = newx( sizeof(LINK)*LINK_ALLOC_SIZE );
  512.  
  513.    else if (num_link%LINK_ALLOC_SIZE == 0)
  514.       a_link = renewx(a_link, sizeof(LINK) * (num_link+LINK_ALLOC_SIZE) );
  515.  
  516.    a_link[num_link] = *l;
  517.  
  518.    return( num_link++ );
  519.    }
  520.  
  521.  
  522. #define PAGE_ALLOC_SIZE (4)
  523.  
  524.  
  525. int add_page(TOPIC *t, PAGE *p)
  526.    {
  527.    if (t->num_page == 0)
  528.       t->page = newx( sizeof(PAGE)*PAGE_ALLOC_SIZE );
  529.  
  530.    else if (t->num_page%PAGE_ALLOC_SIZE == 0)
  531.       t->page = renewx(t->page, sizeof(PAGE) * (t->num_page+PAGE_ALLOC_SIZE) );
  532.  
  533.    t->page[t->num_page] = *p;
  534.  
  535.    return ( t->num_page++ );
  536.    }
  537.  
  538.  
  539. #define TOPIC_ALLOC_SIZE (16)
  540.  
  541.  
  542. int add_topic(TOPIC *t)
  543.    {
  544.    if (num_topic == 0)
  545.       topic = newx( sizeof(TOPIC)*TOPIC_ALLOC_SIZE );
  546.  
  547.    else if (num_topic%TOPIC_ALLOC_SIZE == 0)
  548.       topic = renewx(topic, sizeof(TOPIC) * (num_topic+TOPIC_ALLOC_SIZE) );
  549.  
  550.    topic[num_topic] = *t;
  551.  
  552.    return ( num_topic++ );
  553.    }
  554.  
  555.  
  556. #define LABEL_ALLOC_SIZE (16)
  557.  
  558.  
  559. int add_label(LABEL *l)
  560.    {
  561.    if (l->name[0] == '@')    /* if it's a private label... */
  562.       {
  563.       if (num_plabel == 0)
  564.      plabel = newx( sizeof(LABEL)*LABEL_ALLOC_SIZE );
  565.  
  566.       else if (num_plabel%LABEL_ALLOC_SIZE == 0)
  567.      plabel = renewx(plabel, sizeof(LABEL) * (num_plabel+LABEL_ALLOC_SIZE) );
  568.  
  569.       plabel[num_plabel] = *l;
  570.  
  571.       return ( num_plabel++ );
  572.       }
  573.    else
  574.       {
  575.       if (num_label == 0)
  576.      label = newx( sizeof(LABEL)*LABEL_ALLOC_SIZE );
  577.  
  578.       else if (num_label%LABEL_ALLOC_SIZE == 0)
  579.      label = renewx(label, sizeof(LABEL) * (num_label+LABEL_ALLOC_SIZE) );
  580.  
  581.       label[num_label] = *l;
  582.  
  583.       return ( num_label++ );
  584.       }
  585.    }
  586.  
  587.  
  588. #define CONTENTS_ALLOC_SIZE (16)
  589.  
  590.  
  591. int add_content(CONTENT *c)
  592.    {
  593.    if (num_contents == 0)
  594.       contents = newx( sizeof(CONTENT)*CONTENTS_ALLOC_SIZE );
  595.  
  596.    else if (num_contents%CONTENTS_ALLOC_SIZE == 0)
  597.       contents = renewx(contents, sizeof(CONTENT) * (num_contents+CONTENTS_ALLOC_SIZE) );
  598.  
  599.    contents[num_contents] = *c;
  600.  
  601.    return ( num_contents++ );
  602.    }
  603.  
  604.  
  605. /*
  606.  * read_char() stuff
  607.  */
  608.  
  609.  
  610. #define READ_CHAR_BUFF_SIZE (32)
  611.  
  612.  
  613. int  read_char_buff[READ_CHAR_BUFF_SIZE];
  614. int  read_char_buff_pos = -1;
  615. int  read_char_sp       = 0;
  616.  
  617.  
  618. void unread_char(int ch)
  619.    /*
  620.     * Will not handle new-lines or tabs correctly!
  621.     */
  622.    {
  623.    if (read_char_buff_pos+1 >= READ_CHAR_BUFF_SIZE)
  624.       fatal(0,"Compiler Error -- Read char buffer overflow!");
  625.  
  626.    read_char_buff[++read_char_buff_pos] = ch;
  627.  
  628.    --srccol;
  629.    }
  630.  
  631.  
  632. void unread_string(char *s)
  633.    {
  634.    int p = strlen(s);
  635.  
  636.    while (p-- > 0)
  637.       unread_char(s[p]);
  638.    }
  639.  
  640.  
  641. int eos(void)     /* end-of-source ? */
  642.    {
  643.    return ( !((read_char_sp==0) && (read_char_buff_pos==0) && feof(srcfile)) );
  644.    }
  645.  
  646.  
  647. int _read_char(void)
  648.    {
  649.    int ch;
  650.  
  651.    if (srcline <= 0)
  652.       {
  653.       srcline = 1;
  654.       srccol = 0;
  655.       }
  656.  
  657.    if (read_char_buff_pos >= 0)
  658.       {
  659.       ++srccol;
  660.       return ( read_char_buff[read_char_buff_pos--] );
  661.       }
  662.  
  663.    if (read_char_sp > 0)
  664.       {
  665.       --read_char_sp;
  666.       return (' ');
  667.       }
  668.  
  669.    if ( feof(srcfile) )
  670.       return (-1);
  671.  
  672.    while (1)
  673.       {
  674.       ch = getc(srcfile);
  675.  
  676.       switch (ch)
  677.      {
  678.      case '\t':    /* expand a tab */
  679.         {
  680.         int diff = ( ( (srccol/8) + 1 ) * 8 ) - srccol;
  681.  
  682.         srccol += diff;
  683.         read_char_sp += diff;
  684.         break;
  685.         }
  686.  
  687.      case ' ':
  688.         ++srccol;
  689.         ++read_char_sp;
  690.         break;
  691.  
  692.      case '\n':
  693.         read_char_sp = 0;   /* delete spaces before a \n */
  694.         srccol = 0;
  695.         ++srcline;
  696.         return ('\n');
  697.  
  698.      case -1:            /* EOF */
  699.         if (read_char_sp > 0)
  700.            {
  701.            --read_char_sp;
  702.            return (' ');
  703.            }
  704.         return (-1);
  705.  
  706.      default:
  707.         if (read_char_sp > 0)
  708.            {
  709.            ungetc(ch, srcfile);
  710.            --read_char_sp;
  711.            return (' ');
  712.            }
  713.  
  714.         ++srccol;
  715.         return (ch);
  716.  
  717.      } /* switch */
  718.       }
  719.    }
  720.  
  721.  
  722. int read_char(void)
  723.    {
  724.    int ch;
  725.  
  726.    ch = _read_char();
  727.  
  728.    while (ch == ';' && srccol==1)    /* skip over comments */
  729.       {
  730.       ch = _read_char();
  731.  
  732.       while (ch!='\n' && ch!=-1 )
  733.      ch = _read_char();
  734.  
  735.       ch = _read_char();
  736.       }
  737.  
  738.    if (ch == '\\')   /* process an escape code */
  739.       {
  740.       ch = _read_char();
  741.  
  742.       if (ch >= '0' && ch <= '9')
  743.      {
  744.      char buff[4];
  745.      int  ctr;
  746.  
  747.      for (ctr=0; ; ctr++)
  748.         {
  749.         if ( ch<'0' || ch>'9' || ch==-1 || ctr>=3 )
  750.            {
  751.            unread_char(ch);
  752.            break;
  753.            }
  754.         buff[ctr] = ch;
  755.         ch = _read_char();
  756.         }
  757.      buff[ctr] = '\0';
  758.      ch = atoi(buff);
  759.      }
  760.  
  761.       ch |= 0x100;
  762.       }
  763.  
  764.    if ( (ch & 0xFF) == 0 )
  765.       {
  766.       error(0,"Null character (\'\\0\') not allowed!");
  767.       ch = 0x1FF; /* since we've had an error the file will not be written; */
  768.           /*   the value we return doesn't really matter */
  769.       }
  770.  
  771.    return(ch);
  772.    }
  773.  
  774.  
  775. /*
  776.  * misc. search functions.
  777.  */
  778.  
  779.  
  780. LABEL *find_label(char *name)
  781.    {
  782.    int      l;
  783.    LABEL *lp;
  784.  
  785.    if (*name == '@')
  786.       {
  787.       for (l=0, lp=plabel; l<num_plabel; l++, lp++)
  788.      if ( strcmp(name, lp->name) == 0 )
  789.         return (lp);
  790.       }
  791.    else
  792.       {
  793.       for (l=0, lp=label; l<num_label; l++, lp++)
  794.      if ( strcmp(name, lp->name) == 0 )
  795.         return (lp);
  796.       }
  797.  
  798.    return (NULL);
  799.    }
  800.  
  801.  
  802. int find_topic_title(char *title)
  803.    {
  804.    int t;
  805.    int len;
  806.  
  807.    while (*title == ' ')
  808.       ++title;
  809.  
  810.    len = strlen(title) - 1;
  811.    while ( title[len] == ' ' && len > 0 )
  812.       --len;
  813.  
  814.    ++len;
  815.  
  816.    if ( len > 2 && title[0] == '\"' && title[len-1] == '\"' )
  817.       {
  818.       ++title;
  819.       len -= 2;
  820.       }
  821.  
  822.    for (t=0; t<num_topic; t++)
  823.       if ( strlen(topic[t].title) == len &&
  824.        strnicmp(title, topic[t].title, len) == 0 )
  825.      return (t);
  826.  
  827.    return (-1);   /* not found */
  828.    }
  829.  
  830.  
  831. /*
  832.  * .SRC file parser stuff
  833.  */
  834.  
  835.  
  836. int validate_label_name(char *name)
  837.    {
  838.    if ( !isalpha(*name) && *name!='@' && *name!='_' )
  839.       return (0);  /* invalid */
  840.  
  841.    while (*(++name) != '\0')
  842.       if ( !isalpha(*name) && !isdigit(*name) && *name!='_' )
  843.      return(0);  /* invalid */
  844.  
  845.    return (1);  /* valid */
  846.    }
  847.  
  848.  
  849. char *read_until(char *buff, int len, char *stop_chars)
  850.    {
  851.    int ch;
  852.  
  853.    while ( --len > 0 )
  854.       {
  855.       ch = read_char();
  856.  
  857.       if ( ch == -1 )
  858.      {
  859.      *buff++ = '\0';
  860.      break;
  861.      }
  862.  
  863.       if ( (ch&0xFF) <= MAX_CMD )
  864.      *buff++ = CMD_LITERAL;
  865.  
  866.       *buff++ = ch;
  867.  
  868.       if ( (ch&0x100)==0 && strchr(stop_chars, ch) != NULL )
  869.      break;
  870.       }
  871.  
  872.    return ( buff-1 );
  873.    }
  874.  
  875.  
  876. void skip_over(char *skip)
  877.    {
  878.    int ch;
  879.  
  880.    while (1)
  881.       {
  882.       ch = read_char();
  883.  
  884.       if ( ch == -1 )
  885.      break;
  886.  
  887.       else if ( (ch&0x100) == 0 && strchr(skip, ch) == NULL )
  888.      {
  889.      unread_char(ch);
  890.      break;
  891.      }
  892.       }
  893.    }
  894.  
  895.  
  896. char *pchar(int ch)
  897.    {
  898.    static char buff[16];
  899.  
  900.    if ( ch >= 0x20 && ch <= 0x7E )
  901.       sprintf(buff, "\'%c\'", ch);
  902.    else
  903.       sprintf(buff, "\'\\x%02X\'", ch&0xFF);
  904.  
  905.    return (buff);
  906.    }
  907.  
  908.  
  909. void put_spaces(int how_many)
  910.    {
  911.    if (how_many > 2 && compress_spaces)
  912.       {
  913.       if (how_many > 255)
  914.      {
  915.      error(0,"Too many spaces (over 255).");
  916.      how_many = 255;
  917.      }
  918.  
  919.       *curr++ = CMD_SPACE;
  920.       *curr++ = (BYTE)how_many;
  921.       }
  922.    else
  923.       {
  924.       while (how_many-- > 0)
  925.      *curr++ = ' ';
  926.       }
  927.    }
  928.  
  929.  
  930. int get_next_item(void)   /* used by parse_contents() */
  931.    {
  932.    int     last;
  933.    char *ptr;
  934.  
  935.    skip_over(" \t\n");
  936.    ptr = read_until(cmd, 128, ",}");
  937.    last = (*ptr == '}');
  938.    --ptr;
  939.    while ( ptr >= cmd && strchr(" \t\n",*ptr) )   /* strip trailing spaces */
  940.       --ptr;
  941.    *(++ptr) = '\0';
  942.  
  943.    return (last);
  944.    }
  945.  
  946.  
  947. void process_contents(void)
  948.    {
  949.    CONTENT c;
  950.    char   *ptr;
  951.    int       indent;
  952.    int       ch;
  953.    TOPIC   t;
  954.  
  955.    t.flags     = 0;
  956.    t.title_len = strlen(DOCCONTENTS_TITLE)+1;
  957.    t.title     = dupstr(DOCCONTENTS_TITLE, t.title_len);
  958.    t.doc_page  = -1;
  959.    t.num_page  = 0;
  960.  
  961.    curr = buffer;
  962.  
  963.    c.flags = 0;
  964.    c.id = dupstr("",1);
  965.    c.name = dupstr("",1);
  966.    c.doc_page = -1;
  967.    c.page_num_pos = 0;
  968.    c.num_topic = 1;
  969.    c.is_label[0] = 0;
  970.    c.topic_name[0] = dupstr(DOCCONTENTS_TITLE,0);
  971.    c.srcline = -1;
  972.    add_content(&c);
  973.  
  974.    while (1)
  975.       {
  976.       ch = read_char();
  977.  
  978.       if (ch == '{')   /* process a CONTENT entry */
  979.      {
  980.      int last;
  981.  
  982.      c.flags = 0;
  983.      c.num_topic = 0;
  984.      c.doc_page = -1;
  985.      c.srcfile = src_cfname;
  986.      c.srcline = srcline;
  987.  
  988.      if ( get_next_item() )
  989.         {
  990.         error(0,"Unexpected end of DocContent entry.");
  991.         continue;
  992.         }
  993.      c.id = dupstr(cmd,0);
  994.  
  995.      if ( get_next_item() )
  996.         {
  997.         error(0,"Unexpected end of DocContent entry.");
  998.         continue;
  999.         }
  1000.      indent = atoi(cmd);
  1001.  
  1002.      last = get_next_item();
  1003.  
  1004.      if ( cmd[0] == '\"' )
  1005.         {
  1006.         ptr = cmd+1;
  1007.         if (ptr[strlen(ptr)-1] == '\"')
  1008.            ptr[strlen(ptr)-1] = '\0';
  1009.         else
  1010.            warn(0,"Missing ending quote.");
  1011.  
  1012.         c.is_label[c.num_topic] = 0;
  1013.         c.topic_name[c.num_topic] = dupstr(ptr,0);
  1014.         ++c.num_topic;
  1015.         c.name = dupstr(ptr,0);
  1016.         }
  1017.      else
  1018.         c.name = dupstr(cmd,0);
  1019.  
  1020.      /* now, make the entry in the buffer */
  1021.  
  1022.      sprintf(curr, "%-5s %*.0s%s", c.id, indent*2, "", c.name);
  1023.      ptr = curr + strlen(curr);
  1024.      while ( (ptr-curr) < PAGE_WIDTH-10 )
  1025.         *ptr++ = '.';
  1026.      c.page_num_pos = (unsigned) ( (ptr-3) - buffer );
  1027.      curr = ptr;
  1028.  
  1029.      while (!last)
  1030.         {
  1031.         last = get_next_item();
  1032.  
  1033.         if ( stricmp(cmd, "FF") == 0 )
  1034.            {
  1035.            if ( c.flags & CF_NEW_PAGE )
  1036.           warn(0,"FF already present in this entry.");
  1037.            c.flags |= CF_NEW_PAGE;
  1038.            continue;
  1039.            }
  1040.  
  1041.         if (cmd[0] == '\"')
  1042.            {
  1043.            ptr = cmd+1;
  1044.            if (ptr[strlen(ptr)-1] == '\"')
  1045.           ptr[strlen(ptr)-1] = '\0';
  1046.            else
  1047.           warn(0,"Missing ending quote.");
  1048.  
  1049.            c.is_label[c.num_topic] = 0;
  1050.            c.topic_name[c.num_topic] = dupstr(ptr,0);
  1051.            }
  1052.         else
  1053.            {
  1054.            c.is_label[c.num_topic] = 1;
  1055.            c.topic_name[c.num_topic] = dupstr(cmd,0);
  1056.            }
  1057.  
  1058.         if ( ++c.num_topic >= MAX_CONTENT_TOPIC )
  1059.            {
  1060.            error(0,"Too many topics in DocContent entry.");
  1061.            break;
  1062.            }
  1063.         }
  1064.  
  1065.      add_content(&c);
  1066.      }
  1067.  
  1068.       else if (ch == '~')   /* end at any command */
  1069.      {
  1070.      unread_char(ch);
  1071.      break;
  1072.      }
  1073.  
  1074.       else
  1075.      *curr++ = ch;
  1076.  
  1077.       CHK_BUFFER(0);
  1078.       }
  1079.  
  1080.    alloc_topic_text(&t, (unsigned) (curr - buffer) );
  1081.    add_topic(&t);
  1082.    }
  1083.  
  1084.  
  1085. int parse_link(void)   /* returns length of link or 0 on error */
  1086.    {
  1087.    char *ptr;
  1088.    char *end;
  1089.    int     bad = 0;
  1090.    int     len;
  1091.    LINK  l;
  1092.    int     lnum;
  1093.    int     err_off;
  1094.  
  1095.    l.srcfile  = src_cfname;
  1096.    l.srcline  = srcline;
  1097.    l.doc_page = -1;
  1098.  
  1099.    end = read_until(cmd, 128, "}\n");   /* get the entire hot-link */
  1100.  
  1101.    if (*end == '\0')
  1102.       {
  1103.       error(0,"Unexpected EOF in hot-link.");
  1104.       return (0);
  1105.       }
  1106.  
  1107.    if (*end == '\n')
  1108.       {
  1109.       err_off = 1;
  1110.       warn(1,"Hot-link has no closing curly-brace (\'}\').");
  1111.       }
  1112.    else
  1113.       err_off = 0;
  1114.  
  1115.    *end = '\0';
  1116.  
  1117.    if (cmd[0] == '=')   /* it's an "explicit" link to a label or "special" */
  1118.       {
  1119.       ptr = strchr(cmd, ' ');
  1120.  
  1121.       if (ptr == NULL)
  1122.      ptr = end;
  1123.       else
  1124.      *ptr++ = '\0';
  1125.  
  1126.       len = (int) (end - ptr);
  1127.  
  1128.       if ( cmd[1] == '-' )
  1129.      {
  1130.      l.type      = 2;       /* type 2 = "special" */
  1131.      l.topic_num = atoi(cmd+1);
  1132.      l.topic_off = 0;
  1133.      l.name      = NULL;
  1134.      }
  1135.       else
  1136.      {
  1137.      l.type = 1;           /* type 1 = to a label */
  1138.      if (strlen(cmd) > 32)
  1139.         warn(err_off, "Label is long.");
  1140.      if (cmd[1] == '\0')
  1141.         {
  1142.         error(err_off, "Explicit hot-link has no Label.");
  1143.         bad = 1;
  1144.         }
  1145.      else
  1146.         l.name = dupstr(cmd+1,0);
  1147.      }
  1148.       if (len == 0)
  1149.      warn(err_off, "Explicit hot-link has no title.");
  1150.       }
  1151.    else
  1152.       {
  1153.       ptr = cmd;
  1154.       l.type = 0;   /* type 0 = topic title */
  1155.       len = (int) (end - ptr);
  1156.       if (len == 0)
  1157.      {
  1158.      error(err_off, "Implicit hot-link has no title.");
  1159.      bad = 1;
  1160.      }
  1161.       l.name = dupstr(ptr,len+1);
  1162.       l.name[len] = '\0';
  1163.       }
  1164.  
  1165.    if ( !bad )
  1166.       {
  1167.       CHK_BUFFER(1+3*sizeof(int)+len+1)
  1168.       lnum = add_link(&l);
  1169.       *curr++ = CMD_LINK;
  1170.       setint(curr,lnum);
  1171.       curr += 3*sizeof(int);
  1172.       memcpy(curr, ptr, len);
  1173.       curr += len;
  1174.       *curr++ = CMD_LINK;
  1175.       return (len);
  1176.       }
  1177.    else
  1178.       return (0);
  1179.    }
  1180.  
  1181.  
  1182. #define MAX_TABLE_SIZE (100)
  1183.  
  1184.  
  1185. int create_table(void)
  1186.    {
  1187.    char  *ptr;
  1188.    int      width;
  1189.    int      cols;
  1190.    int      start_off;
  1191.    int      first_link;
  1192.    int      rows;
  1193.    int      r, c;
  1194.    int      ch;
  1195.    int      done;
  1196.    int      len;
  1197.    int      lnum;
  1198.    int      count;
  1199.    char  *title[MAX_TABLE_SIZE];
  1200.    char  *table_start;
  1201.  
  1202.    ptr = strchr(cmd, '=');
  1203.  
  1204.    if (ptr == NULL)
  1205.       return (0);   /* should never happen! */
  1206.  
  1207.    ptr++;
  1208.  
  1209.    len = sscanf(ptr, " %d %d %d", &width, &cols, &start_off);
  1210.  
  1211.    if (len < 3)
  1212.       {
  1213.       error(1,"Too few arguments to Table.");
  1214.       return (0);
  1215.       }
  1216.  
  1217.    if (width<=0 || width > 78 || cols<=0 || start_off<0 || start_off > 78)
  1218.       {
  1219.       error(1,"Argument out of range.");
  1220.       return (0);
  1221.       }
  1222.  
  1223.    done = 0;
  1224.  
  1225.    first_link = num_link;
  1226.    table_start = curr;
  1227.    count = 0;
  1228.  
  1229.    /* first, read all the links in the table */
  1230.  
  1231.    do
  1232.       {
  1233.  
  1234.       do
  1235.      ch = read_char();
  1236.       while ( ch=='\n' || ch == ' ' );
  1237.  
  1238.       if (done)
  1239.      break;
  1240.  
  1241.       switch (ch)
  1242.      {
  1243.      case -1:
  1244.         error(0,"Unexpected EOF in a Table.");
  1245.         return(0);
  1246.  
  1247.      case '{':
  1248.         if (count >= MAX_TABLE_SIZE)
  1249.            fatal(0,"Table is too large.");
  1250.         len = parse_link();
  1251.         curr = table_start;   /* reset to the start... */
  1252.             title[count] = dupstr(curr+3*sizeof(int)+1, len+1);
  1253.         if (len >= width)
  1254.            {
  1255.            warn(1,"Link is too long; truncating.");
  1256.            len = width-1;
  1257.            }
  1258.         title[count][len] = '\0';
  1259.         ++count;
  1260.         break;
  1261.  
  1262.      case '~':
  1263.         {
  1264.         int imbedded;
  1265.  
  1266.         ch = read_char();
  1267.  
  1268.         if (ch=='(')
  1269.            imbedded = 1;
  1270.         else
  1271.            {
  1272.            imbedded = 0;
  1273.            unread_char(ch);
  1274.            }
  1275.  
  1276.         ptr = read_until(cmd, 128, ")\n,");
  1277.  
  1278.         ch = *ptr;
  1279.         *ptr = '\0';
  1280.  
  1281.         if  ( stricmp(cmd, "EndTable") == 0 )
  1282.            done = 1;
  1283.         else
  1284.            {
  1285.            error(1,"Unexpected command in table \"%s\"", cmd);
  1286.            warn(1,"Command will be ignored.");
  1287.            }
  1288.  
  1289.         if (ch == ',')
  1290.            {
  1291.            if (imbedded)
  1292.           unread_char('(');
  1293.            unread_char('~');
  1294.            }
  1295.         }
  1296.         break;
  1297.  
  1298.      default:
  1299.         error(0,"Unexpected character %s.", pchar(ch));
  1300.         break;
  1301.      }
  1302.       }
  1303.    while (!done);
  1304.  
  1305.    /* now, put all the links into the buffer... */
  1306.  
  1307.    rows = 1 + ( count / cols );
  1308.  
  1309.    for (r=0; r<rows; r++)
  1310.       {
  1311.       put_spaces(start_off);
  1312.       for (c=0; c<cols; c++)
  1313.      {
  1314.      lnum = c*rows + r;
  1315.  
  1316.      if ( first_link+lnum >= num_link )
  1317.         break;
  1318.  
  1319.      len = strlen(title[lnum]);
  1320.      *curr++ = CMD_LINK;
  1321.          setint(curr,first_link+lnum);
  1322.          curr += 3*sizeof(int);
  1323.      memcpy(curr, title[lnum], len);
  1324.      curr += len;
  1325.      *curr++ = CMD_LINK;
  1326.  
  1327.      delete(title[lnum]);
  1328.  
  1329.      if ( c < cols-1 )
  1330.         put_spaces( width-len );
  1331.      }
  1332.       *curr++ = '\n';
  1333.       }
  1334.  
  1335.    return (1);
  1336.    }
  1337.  
  1338.  
  1339. void process_comment(void)
  1340.    {
  1341.    int ch;
  1342.  
  1343.    while ( 1 )
  1344.       {
  1345.       ch = read_char();
  1346.  
  1347.       if (ch == '~')
  1348.      {
  1349.      int   imbedded;
  1350.      char *ptr;
  1351.  
  1352.      ch = read_char();
  1353.  
  1354.      if (ch=='(')
  1355.         imbedded = 1;
  1356.      else
  1357.         {
  1358.         imbedded = 0;
  1359.         unread_char(ch);
  1360.         }
  1361.  
  1362.      ptr = read_until(cmd, 128, ")\n,");
  1363.  
  1364.      ch = *ptr;
  1365.      *ptr = '\0';
  1366.  
  1367.      if  ( stricmp(cmd, "EndComment") == 0 )
  1368.         {
  1369.         if (ch == ',')
  1370.            {
  1371.            if (imbedded)
  1372.           unread_char('(');
  1373.            unread_char('~');
  1374.            }
  1375.         break;
  1376.         }
  1377.      }
  1378.  
  1379.       else if ( ch == -1 )
  1380.      {
  1381.      error(0,"Unexpected EOF in Comment");
  1382.      break;
  1383.      }
  1384.       }
  1385.    }
  1386.  
  1387.  
  1388. void process_bininc(void)
  1389.    {
  1390.    int  handle;
  1391.    long len;
  1392.  
  1393.    if ( (handle=open(cmd+7, O_RDONLY|O_BINARY)) == -1 )
  1394.       {
  1395.       error(0,"Unable to open \"%s\"", cmd+7);
  1396.       return ;
  1397.       }
  1398.  
  1399.    len = filelength(handle);
  1400.  
  1401.    if ( len >= BUFFER_SIZE )
  1402.       {
  1403.       error(0,"File \"%s\" is too large to BinInc (%dK).", cmd+7, (int)(len>>10));
  1404.       close(handle);
  1405.       return ;
  1406.       }
  1407.  
  1408.    /*
  1409.     * Since we know len is less than BUFFER_SIZE (and therefore less then
  1410.     * 64K) we can treat it as an unsigned.
  1411.     */
  1412.  
  1413.    CHK_BUFFER((unsigned)len);
  1414.  
  1415.    read(handle, curr, (unsigned)len);
  1416.  
  1417.    curr += (unsigned)len;
  1418.  
  1419.    close(handle);
  1420.    }
  1421.  
  1422.  
  1423. void start_topic(TOPIC *t, char *title, int title_len)
  1424.    {
  1425.    t->flags = 0;
  1426.    t->title_len = title_len;
  1427.    t->title = dupstr(title, title_len+1);
  1428.    t->title[title_len] = '\0';
  1429.    t->doc_page = -1;
  1430.    t->num_page = 0;
  1431.    curr = buffer;
  1432.    }
  1433.  
  1434.  
  1435. void end_topic(TOPIC *t)
  1436.    {
  1437.    alloc_topic_text(t, (unsigned) (curr - buffer) );
  1438.    add_topic(t);
  1439.    }
  1440.  
  1441.  
  1442. int end_of_sentence(char *ptr)  /* true if ptr is at the end of a sentence */
  1443.    {
  1444.    if ( *ptr == ')')
  1445.       --ptr;
  1446.  
  1447.    if ( *ptr == '\"')
  1448.       --ptr;
  1449.  
  1450.    return ( *ptr=='.' || *ptr=='?' || *ptr=='!' );
  1451.    }
  1452.  
  1453.  
  1454. void add_blank_for_split(void)     /* add space at curr for merging two lines */
  1455.    {
  1456.    if ( !is_hyphen(curr-1) )   /* no spaces if it's a hyphen */
  1457.       {
  1458.       if ( end_of_sentence(curr-1) )
  1459.      *curr++ = ' ';  /* two spaces at end of a sentence */
  1460.       *curr++ = ' ';
  1461.       }
  1462.    }
  1463.  
  1464.  
  1465. void put_a_char(int ch, TOPIC *t)
  1466.    {
  1467.    if (ch == '{' && !(t->flags & TF_DATA) )   /* is it a hot-link? */
  1468.       parse_link();
  1469.    else
  1470.       {
  1471.       if ( (ch&0xFF) <= MAX_CMD)
  1472.      *curr++ = CMD_LITERAL;
  1473.       *curr++ = ch;
  1474.       }
  1475.    }
  1476.  
  1477.  
  1478. enum STATES   /* states for FSM's */
  1479.    {
  1480.    S_Start,            /* initial state, between paragraphs       */
  1481.    S_StartFirstLine,        /* spaces at start of first line           */
  1482.    S_FirstLine,         /* text on the first line               */
  1483.    S_FirstLineSpaces,        /* spaces on the first line            */
  1484.    S_StartSecondLine,        /* spaces at start of second line           */
  1485.    S_Line,            /* text on lines after the first           */
  1486.    S_LineSpaces,        /* spaces on lines after the first           */
  1487.    S_StartLine,         /* spaces at start of lines after second       */
  1488.    S_FormatDisabled,        /* format automatically disabled for this line */
  1489.    S_FormatDisabledSpaces,  /* spaces in line which format is disabled       */
  1490.    S_Spaces
  1491.    } ;
  1492.  
  1493.  
  1494. void check_command_length(int eoff, int len)
  1495.    {
  1496.    if (strlen(cmd) != len)
  1497.       error(eoff, "Invalid text after a command \"%s\"", cmd+len);
  1498.    }
  1499.  
  1500.  
  1501. void read_src(char *fname)
  1502.    {
  1503.    int      ch;
  1504.    char  *ptr;
  1505.    TOPIC  t;
  1506.    LABEL  lbl;
  1507.    char  *margin_pos = NULL;
  1508.    int      in_topic   = 0,
  1509.       formatting = 1,
  1510.       state      = S_Start,
  1511.       num_spaces = 0,
  1512.       margin     = 0,
  1513.       in_para    = 0,
  1514.       centering  = 0,
  1515.       lformat_exclude = format_exclude,
  1516.       again;
  1517.  
  1518.    xonline = xdoc = 0;
  1519.  
  1520.    src_cfname = fname;
  1521.  
  1522.    if ( (srcfile = fopen(fname, "rt")) == NULL )
  1523.       fatal(0,"Unable to open \"%s\"", fname);
  1524.  
  1525.    msg("Compiling: %s", fname);
  1526.  
  1527.    in_topic = 0;
  1528.  
  1529.    curr = buffer;
  1530.  
  1531.    while ( 1 )
  1532.       {
  1533.  
  1534.       ch = read_char();
  1535.  
  1536.       if ( ch == -1 )   /* EOF? */
  1537.      {
  1538.      if ( include_stack_top >= 0)
  1539.         {
  1540.         fclose(srcfile);
  1541.         src_cfname = include_stack[include_stack_top].fname;
  1542.         srcfile = include_stack[include_stack_top].file;
  1543.         srcline = include_stack[include_stack_top].line;
  1544.         srccol  = include_stack[include_stack_top].col;
  1545.         --include_stack_top;
  1546.         continue;
  1547.         }
  1548.      else
  1549.         {
  1550.         if (in_topic)  /* if we're in a topic, finish it */
  1551.            end_topic(&t);
  1552.         if (num_topic == 0)
  1553.            warn(0,".SRC file has no topics.");
  1554.         break;
  1555.         }
  1556.      }
  1557.  
  1558.       if (ch == '~')   /* is is a command? */
  1559.      {
  1560.      int imbedded;
  1561.      int eoff;
  1562.      int done;
  1563.  
  1564.      ch = read_char();
  1565.      if (ch == '(')
  1566.         {
  1567.         imbedded = 1;
  1568.         eoff = 0;
  1569.         }
  1570.      else
  1571.         {
  1572.         imbedded = 0;
  1573.         eoff=0;
  1574.         unread_char(ch);
  1575.         }
  1576.  
  1577.      done = 0;
  1578.  
  1579.      while ( !done )
  1580.         {
  1581.         do
  1582.            ch = read_char();
  1583.         while (ch == ' ');
  1584.         unread_char(ch);
  1585.  
  1586.         if (imbedded)
  1587.            ptr = read_until(cmd, 128, ")\n,");
  1588.         else
  1589.            ptr = read_until(cmd, 128, "\n,");
  1590.  
  1591.         done = 1;
  1592.  
  1593.         if ( *ptr == '\0' )
  1594.            {
  1595.            error(0,"Unexpected EOF in command.");
  1596.            break;
  1597.            }
  1598.  
  1599.         if (*ptr == '\n')
  1600.            ++eoff;
  1601.  
  1602.         if ( imbedded && *ptr == '\n' )
  1603.            error(eoff,"Imbedded command has no closing parend (\')\')");
  1604.  
  1605.         done = (*ptr != ',');   /* we done if it's not a comma */
  1606.  
  1607.         if ( *ptr != '\n' && *ptr != ')' && *ptr != ',' )
  1608.            {
  1609.            error(0,"Command line too long.");
  1610.            break;
  1611.            }
  1612.  
  1613.         *ptr = '\0';
  1614.  
  1615.  
  1616.         /* commands allowed anytime... */
  1617.  
  1618.         if ( strnicmp(cmd, "Topic=", 6) == 0 )
  1619.            {
  1620.            if (in_topic)  /* if we're in a topic, finish it */
  1621.           end_topic(&t);
  1622.            else
  1623.           in_topic = 1;
  1624.  
  1625.            if (cmd[6] == '\0')
  1626.           warn(eoff,"Topic has no title.");
  1627.  
  1628.            else if (strlen(cmd+6) > 70)
  1629.           error(eoff,"Topic title is too long.");
  1630.  
  1631.            else if (strlen(cmd+6) > 60)
  1632.           warn(eoff,"Topic title is long.");
  1633.  
  1634.            if ( find_topic_title(cmd+6) != -1 )
  1635.           error(eoff,"Topic title already exists.");
  1636.  
  1637.            start_topic(&t, cmd+6, (unsigned)(ptr-(cmd+6)));
  1638.            formatting = 1;
  1639.            centering = 0;
  1640.            state = S_Start;
  1641.            in_para = 0;
  1642.            num_spaces = 0;
  1643.            xonline = xdoc = 0;
  1644.            lformat_exclude = format_exclude;
  1645.            compress_spaces = 1;
  1646.            continue;
  1647.            }
  1648.  
  1649.         else if ( strnicmp(cmd, "Data=", 5) == 0 )
  1650.            {
  1651.            if (in_topic)  /* if we're in a topic, finish it */
  1652.           end_topic(&t);
  1653.            else
  1654.           in_topic = 1;
  1655.  
  1656.            if (cmd[5] == '\0')
  1657.           warn(eoff,"Data topic has no label.");
  1658.  
  1659.            if ( !validate_label_name(cmd+5) )
  1660.           {
  1661.           error(eoff,"Label \"%s\" contains illegal characters.", cmd+5);
  1662.           continue;
  1663.           }
  1664.  
  1665.            if ( find_label(cmd+5) != NULL )
  1666.           {
  1667.           error(eoff,"Label \"%s\" already exists", cmd+5);
  1668.           continue;
  1669.           }
  1670.  
  1671.            if ( cmd[5] == '@' )
  1672.           warn(eoff, "Data topic has a local label.");
  1673.  
  1674.            start_topic(&t, "", 0);
  1675.            t.flags |= TF_DATA;
  1676.  
  1677.            if (strlen(cmd+5) > 32)
  1678.           warn(eoff,"Label name is long.");
  1679.  
  1680.            lbl.name      = dupstr(cmd+5, 0);
  1681.            lbl.topic_num = num_topic;
  1682.            lbl.topic_off = 0;
  1683.            lbl.doc_page  = -1;
  1684.            add_label(&lbl);
  1685.  
  1686.            formatting = 0;
  1687.            centering = 0;
  1688.            state = S_Start;
  1689.            in_para = 0;
  1690.            num_spaces = 0;
  1691.            xonline = xdoc = 0;
  1692.            lformat_exclude = format_exclude;
  1693.            compress_spaces = 0;
  1694.            continue;
  1695.            }
  1696.  
  1697.         else if ( strnicmp(cmd, "DocContents", 11) == 0 )
  1698.            {
  1699.            check_command_length(eoff, 11);
  1700.            if (in_topic)  /* if we're in a topic, finish it */
  1701.           end_topic(&t);
  1702.            if (!done)
  1703.           {
  1704.           if (imbedded)
  1705.              unread_char('(');
  1706.           unread_char('~');
  1707.           done = 1;
  1708.           }
  1709.            compress_spaces = 1;
  1710.            process_contents();
  1711.            in_topic = 0;
  1712.            continue;
  1713.            }
  1714.  
  1715.         else if ( stricmp(cmd, "Comment") == 0 )
  1716.            {
  1717.            process_comment();
  1718.            continue;
  1719.            }
  1720.  
  1721.         else if ( strnicmp(cmd, "FormatExclude", 13) == 0 )
  1722.            {
  1723.            if (cmd[13] == '-')
  1724.           {
  1725.           check_command_length(eoff, 14);
  1726.           if ( in_topic )
  1727.              {
  1728.              if (lformat_exclude > 0)
  1729.                 lformat_exclude = -lformat_exclude;
  1730.              else
  1731.                 warn(eoff,"\"FormatExclude-\" is already in effect.");
  1732.              }
  1733.           else
  1734.              {
  1735.              if (format_exclude > 0)
  1736.                 format_exclude = -format_exclude;
  1737.              else
  1738.                 warn(eoff,"\"FormatExclude-\" is already in effect.");
  1739.              }
  1740.           }
  1741.            else if (cmd[13] == '+')
  1742.           {
  1743.           check_command_length(eoff,14);
  1744.           if ( in_topic )
  1745.              {
  1746.              if (lformat_exclude < 0)
  1747.                 lformat_exclude = -lformat_exclude;
  1748.              else
  1749.                 warn(eoff,"\"FormatExclude+\" is already in effect.");
  1750.              }
  1751.           else
  1752.              {
  1753.              if (format_exclude < 0)
  1754.                 format_exclude = -format_exclude;
  1755.              else
  1756.                 warn(eoff,"\"FormatExclude+\" is already in effect.");
  1757.              }
  1758.           }
  1759.            else if (cmd[13] == '=')
  1760.           {
  1761.           if (cmd[14] == 'n' || cmd[14] == 'N')
  1762.              {
  1763.              check_command_length(eoff,15);
  1764.              if (in_topic)
  1765.                 lformat_exclude = 0;
  1766.              else
  1767.                format_exclude = 0;
  1768.              }
  1769.           else if (cmd[14] == '\0')
  1770.              lformat_exclude = format_exclude;
  1771.           else
  1772.              {
  1773.              int n = ( ( (in_topic) ? lformat_exclude : format_exclude) < 0 ) ? -1 : 1;
  1774.  
  1775.              lformat_exclude = atoi(cmd+14);
  1776.  
  1777.              if ( lformat_exclude <= 0 )
  1778.                 {
  1779.                 error(eoff,"Invalid argument to FormatExclude=");
  1780.                 lformat_exclude = 0;
  1781.                 }
  1782.  
  1783.              lformat_exclude *= n;
  1784.  
  1785.              if ( !in_topic )
  1786.                 format_exclude = lformat_exclude;
  1787.              }
  1788.           }
  1789.            else
  1790.           error(eoff,"Invalid format for FormatExclude");
  1791.  
  1792.            continue;
  1793.            }
  1794.  
  1795.         else if ( strnicmp(cmd, "Include ", 8) == 0 )
  1796.            {
  1797.            if (include_stack_top >= MAX_INCLUDE_STACK-1)
  1798.           error(eoff, "Too many nested Includes.");
  1799.            else
  1800.           {
  1801.           ++include_stack_top;
  1802.           include_stack[include_stack_top].fname = src_cfname;
  1803.           include_stack[include_stack_top].file = srcfile;
  1804.           include_stack[include_stack_top].line = srcline;
  1805.           include_stack[include_stack_top].col  = srccol;
  1806.           strupr(cmd+8);
  1807.           if ( (srcfile = fopen(cmd+8, "rt")) == NULL )
  1808.              {
  1809.              error(eoff, "Unable to open \"%s\"", cmd+8);
  1810.              srcfile = include_stack[include_stack_top--].file;
  1811.              }
  1812.           src_cfname = dupstr(cmd+8,0);  /* never deallocate! */
  1813.           srcline = 1;
  1814.           srccol = 0;
  1815.           }
  1816.  
  1817.            continue;
  1818.            }
  1819.  
  1820.  
  1821.         /* commands allowed only before all topics... */
  1822.  
  1823.         if ( !in_topic )
  1824.            {
  1825.            if ( strnicmp(cmd, "HdrFile=", 8) == 0 )
  1826.           {
  1827.           if (hdr_fname[0] != '\0')
  1828.              warn(eoff,"Header Filename has already been defined.");
  1829.           strcpy(hdr_fname, cmd+8);
  1830.           strupr(hdr_fname);
  1831.           }
  1832.  
  1833.            else if ( strnicmp(cmd, "HlpFile=", 8) == 0 )
  1834.           {
  1835.           if (hlp_fname[0] != '\0')
  1836.              warn(eoff,"Help Filename has already been defined.");
  1837.           strcpy(hlp_fname, cmd+8);
  1838.           strupr(hlp_fname);
  1839.           }
  1840.  
  1841.            else if ( strnicmp(cmd, "Version=", 8) == 0 )
  1842.           {
  1843.           if (version != -1)   /* an unlikely value */
  1844.              warn(eoff,"Help version has already been defined");
  1845.           version = atoi(cmd+8);
  1846.           }
  1847.  
  1848.            else
  1849.           error(eoff,"Bad or unexpected command \"%s\"", cmd);
  1850.  
  1851.            continue;
  1852.            }
  1853.  
  1854.  
  1855.         /* commands allowed only in a topic... */
  1856.  
  1857.         else
  1858.            {
  1859.            if (strnicmp(cmd, "FF", 2) == 0 )
  1860.           {
  1861.           check_command_length(eoff,2);
  1862.           if ( in_para )
  1863.              *curr++ = '\n';  /* finish off current paragraph */
  1864.           *curr++ = CMD_FF;
  1865.           state = S_Start;
  1866.           in_para = 0;
  1867.           num_spaces = 0;
  1868.           }
  1869.  
  1870.            else if (strnicmp(cmd, "DocFF", 5) == 0 )
  1871.           {
  1872.           check_command_length(eoff,5);
  1873.           if ( in_para )
  1874.              *curr++ = '\n';  /* finish off current paragraph */
  1875.           if (!xonline)
  1876.              *curr++ = CMD_XONLINE;
  1877.           *curr++ = CMD_FF;
  1878.           if (!xonline)
  1879.              *curr++ = CMD_XONLINE;
  1880.           state = S_Start;
  1881.           in_para = 0;
  1882.           num_spaces = 0;
  1883.           }
  1884.  
  1885.            else if (strnicmp(cmd, "OnlineFF", 8) == 0 )
  1886.           {
  1887.           check_command_length(eoff,8);
  1888.           if ( in_para )
  1889.              *curr++ = '\n';  /* finish off current paragraph */
  1890.           if (!xdoc)
  1891.              *curr++ = CMD_XDOC;
  1892.           *curr++ = CMD_FF;
  1893.           if (!xdoc)
  1894.              *curr++ = CMD_XDOC;
  1895.           state = S_Start;
  1896.           in_para = 0;
  1897.           num_spaces = 0;
  1898.           }
  1899.  
  1900.            else if ( strnicmp(cmd, "Label=", 6) == 0 )
  1901.           {
  1902.           if (strlen(cmd+6) <= 0)
  1903.              error(eoff,"Label has no name.");
  1904.  
  1905.           else if ( !validate_label_name(cmd+6) )
  1906.              error(eoff,"Label \"%s\" contains illegal characters.", cmd+6);
  1907.  
  1908.           else if ( find_label(cmd+6) != NULL )
  1909.              error(eoff,"Label \"%s\" already exists", cmd+6);
  1910.  
  1911.           else
  1912.              {
  1913.              if (strlen(cmd+6) > 32)
  1914.                 warn(eoff,"Label name is long.");
  1915.  
  1916.             if ( (t.flags & TF_DATA) && cmd[6] == '@' )
  1917.                warn(eoff, "Data topic has a local label.");
  1918.  
  1919.              lbl.name       = dupstr(cmd+6, 0);
  1920.              lbl.topic_num = num_topic;
  1921.              lbl.topic_off = (unsigned)(curr - buffer);
  1922.              lbl.doc_page  = -1;
  1923.              add_label(&lbl);
  1924.              }
  1925.           }
  1926.  
  1927.            else if ( strnicmp(cmd, "Table=", 6) == 0 )
  1928.           {
  1929.           if ( in_para )
  1930.              {
  1931.              *curr++ = '\n';  /* finish off current paragraph */
  1932.              in_para = 0;
  1933.              num_spaces = 0;
  1934.              state = S_Start;
  1935.              }
  1936.  
  1937.           if (!done)
  1938.              {
  1939.              if (imbedded)
  1940.                 unread_char('(');
  1941.              unread_char('~');
  1942.              done = 1;
  1943.              }
  1944.  
  1945.           create_table();
  1946.           }
  1947.  
  1948.            else if ( strnicmp(cmd, "FormatExclude", 12) == 0 )
  1949.           {
  1950.           if (cmd[13] == '-')
  1951.              {
  1952.              check_command_length(eoff,14);
  1953.              if (lformat_exclude > 0)
  1954.                 lformat_exclude = -lformat_exclude;
  1955.              else
  1956.                 warn(0,"\"FormatExclude-\" is already in effect.");
  1957.              }
  1958.           else if (cmd[13] == '+')
  1959.              {
  1960.              check_command_length(eoff,14);
  1961.              if (lformat_exclude < 0)
  1962.                 lformat_exclude = -lformat_exclude;
  1963.              else
  1964.                 warn(0,"\"FormatExclude+\" is already in effect.");
  1965.              }
  1966.           else
  1967.              error(eoff,"Unexpected or invalid argument to FormatExclude.");
  1968.           }
  1969.  
  1970.            else if ( strnicmp(cmd, "Format", 6) == 0 )
  1971.           {
  1972.           if (cmd[6] == '+')
  1973.              {
  1974.              check_command_length(eoff,7);
  1975.              if ( !formatting )
  1976.                 {
  1977.                 formatting = 1;
  1978.                 in_para = 0;
  1979.                 num_spaces = 0;
  1980.                 state = S_Start;
  1981.                 }
  1982.              else
  1983.                 warn(eoff,"\"Format+\" is already in effect.");
  1984.              }
  1985.           else if (cmd[6] == '-')
  1986.              {
  1987.              check_command_length(eoff,7);
  1988.              if ( formatting )
  1989.                 {
  1990.                 if ( in_para )
  1991.                *curr++ = '\n';  /* finish off current paragraph */
  1992.                 state = S_Start;
  1993.                 in_para = 0;
  1994.                 formatting = 0;
  1995.                 num_spaces = 0;
  1996.                 state = S_Start;
  1997.                 }
  1998.              else
  1999.                 warn(eoff,"\"Format-\" is already in effect.");
  2000.              }
  2001.           else
  2002.              error(eoff,"Invalid argument to Format.");
  2003.           }
  2004.  
  2005.            else if ( strnicmp(cmd, "Online", 6) == 0 )
  2006.           {
  2007.           if (cmd[6] == '+')
  2008.              {
  2009.              check_command_length(eoff,7);
  2010.  
  2011.              if ( xonline )
  2012.                 {
  2013.                 *curr++ = CMD_XONLINE;
  2014.                 xonline = 0;
  2015.                 }
  2016.              else
  2017.                 warn(eoff,"\"Online+\" already in effect.");
  2018.              }
  2019.           else if (cmd[6] == '-')
  2020.              {
  2021.              check_command_length(eoff,7);
  2022.              if ( !xonline )
  2023.                 {
  2024.                 *curr++ = CMD_XONLINE;
  2025.                 xonline = 1;
  2026.                 }
  2027.              else
  2028.                 warn(eoff,"\"Online-\" already in effect.");
  2029.              }
  2030.           else
  2031.              error(eoff,"Invalid argument to Online.");
  2032.           }
  2033.  
  2034.            else if ( strnicmp(cmd, "Doc", 3) == 0 )
  2035.           {
  2036.           if (cmd[3] == '+')
  2037.              {
  2038.              check_command_length(eoff,4);
  2039.              if ( xdoc )
  2040.                 {
  2041.                 *curr++ = CMD_XDOC;
  2042.                 xdoc = 0;
  2043.                 }
  2044.              else
  2045.                 warn(eoff,"\"Doc+\" already in effect.");
  2046.              }
  2047.           else if (cmd[3] == '-')
  2048.              {
  2049.              check_command_length(eoff,4);
  2050.              if ( !xdoc )
  2051.                 {
  2052.                 *curr++ = CMD_XDOC;
  2053.                 xdoc = 1;
  2054.                 }
  2055.              else
  2056.                 warn(eoff,"\"Doc-\" already in effect.");
  2057.              }
  2058.           else
  2059.              error(eoff,"Invalid argument to Doc.");
  2060.           }
  2061.  
  2062.            else if ( strnicmp(cmd, "Center", 6) == 0 )
  2063.           {
  2064.           if (cmd[6] == '+')
  2065.              {
  2066.              check_command_length(eoff,7);
  2067.              if ( !centering )
  2068.                 {
  2069.                 centering = 1;
  2070.                 if ( in_para )
  2071.                {
  2072.                *curr++ = '\n';
  2073.                in_para = 0;
  2074.                }
  2075.                 state = S_Start;  /* for centering FSM */
  2076.                 }
  2077.              else
  2078.                 warn(eoff,"\"Center+\" already in effect.");
  2079.              }
  2080.           else if (cmd[6] == '-')
  2081.              {
  2082.              check_command_length(eoff,7);
  2083.              if ( centering )
  2084.                 {
  2085.                 centering = 0;
  2086.                 state = S_Start;  /* for centering FSM */
  2087.                 }
  2088.              else
  2089.                 warn(eoff,"\"Center-\" already in effect.");
  2090.              }
  2091.           else
  2092.              error(eoff,"Invalid argument to Center.");
  2093.           }
  2094.  
  2095.            else if ( strnicmp(cmd, "CompressSpaces", 14) == 0 )
  2096.           {
  2097.           check_command_length(eoff,15);
  2098.  
  2099.           if ( cmd[14] == '+' )
  2100.              {
  2101.              if ( compress_spaces )
  2102.                 warn(eoff,"\"CompressSpaces+\" is already in effect.");
  2103.              else
  2104.                 compress_spaces = 1;
  2105.              }
  2106.           else if ( cmd[14] == '-' )
  2107.              {
  2108.              if ( !compress_spaces )
  2109.                 warn(eoff,"\"CompressSpaces-\" is already in effect.");
  2110.              else
  2111.                 compress_spaces = 0;
  2112.              }
  2113.           else
  2114.              error(eoff,"Invalid argument to CompressSpaces.");
  2115.           }
  2116.  
  2117.            else if ( strnicmp("BinInc ", cmd, 7) == 0 )
  2118.           {
  2119.           if ( !(t.flags & TF_DATA) )
  2120.              error(eoff,"BinInc allowed only in Data topics.");
  2121.           else
  2122.              process_bininc();
  2123.           }
  2124.  
  2125.            else
  2126.           error(eoff,"Bad or unexpected command \"%s\".", cmd);
  2127.            } /* else */
  2128.  
  2129.         } /* while (!done) */
  2130.  
  2131.      continue;
  2132.      }
  2133.  
  2134.       if ( !in_topic )
  2135.      {
  2136.      cmd[0] = ch;
  2137.      ptr = read_until(cmd+1, 127, "\n~");
  2138.      if (*ptr == '~')
  2139.         unread_char('~');
  2140.      *ptr = '\0';
  2141.      error(0,"Text outside of any topic \"%s\".", cmd);
  2142.      continue;
  2143.      }
  2144.  
  2145.       if ( centering )
  2146.      {
  2147.      do
  2148.         {
  2149.         again = 0;     /* default */
  2150.  
  2151.         switch (state)
  2152.            {
  2153.            case S_Start:
  2154.           if (ch == ' ')
  2155.              ; /* do nothing */
  2156.           else if ( (ch&0xFF) == '\n' )
  2157.              *curr++ = ch;  /* no need to center blank lines. */
  2158.           else
  2159.              {
  2160.              *curr++ = CMD_CENTER;
  2161.              state = S_Line;
  2162.              again = 1;
  2163.              }
  2164.           break;
  2165.  
  2166.            case S_Line:
  2167.           put_a_char(ch, &t);
  2168.           if ( (ch&0xFF) == '\n')
  2169.              state = S_Start;
  2170.           break;
  2171.            } /* switch */
  2172.         }
  2173.      while (again);
  2174.      }
  2175.  
  2176.       else if ( formatting )
  2177.      {
  2178.      int again;
  2179.  
  2180.      do
  2181.         {
  2182.         again = 0;     /* default */
  2183.  
  2184.         switch (state)
  2185.            {
  2186.            case S_Start:
  2187.           if ( (ch&0xFF) == '\n' )
  2188.              *curr++ = ch;
  2189.           else
  2190.              {
  2191.              state = S_StartFirstLine;
  2192.              num_spaces = 0;
  2193.              again = 1;
  2194.              }
  2195.           break;
  2196.  
  2197.            case S_StartFirstLine:
  2198.           if ( ch == ' ')
  2199.              ++num_spaces;
  2200.  
  2201.           else
  2202.              {
  2203.              if (lformat_exclude > 0 && num_spaces >= lformat_exclude )
  2204.                 {
  2205.                 put_spaces(num_spaces);
  2206.                 num_spaces = 0;
  2207.                 state = S_FormatDisabled;
  2208.                 again = 1;
  2209.                 }
  2210.              else
  2211.                 {
  2212.                 *curr++ = CMD_PARA;
  2213.                 *curr++ = (char)num_spaces;
  2214.                 *curr++ = (char)num_spaces;
  2215.                 margin_pos = curr - 1;
  2216.                 state = S_FirstLine;
  2217.                 again = 1;
  2218.                 in_para = 1;
  2219.                 }
  2220.              }
  2221.           break;
  2222.  
  2223.            case S_FirstLine:
  2224.           if (ch == '\n')
  2225.              {
  2226.              state = S_StartSecondLine;
  2227.              num_spaces = 0;
  2228.              }
  2229.           else if (ch == ('\n'|0x100) )   /* force end of para ? */
  2230.              {
  2231.              *curr++ = '\n';
  2232.              in_para = 0;
  2233.              state = S_Start;
  2234.              }
  2235.           else if ( ch == ' ' )
  2236.              {
  2237.              state = S_FirstLineSpaces;
  2238.              num_spaces = 1;
  2239.              }
  2240.           else
  2241.              put_a_char(ch, &t);
  2242.           break;
  2243.  
  2244.            case S_FirstLineSpaces:
  2245.           if (ch == ' ')
  2246.              ++num_spaces;
  2247.           else
  2248.              {
  2249.              put_spaces(num_spaces);
  2250.              state = S_FirstLine;
  2251.              again = 1;
  2252.              }
  2253.           break;
  2254.  
  2255.            case S_StartSecondLine:
  2256.           if ( ch == ' ')
  2257.              ++num_spaces;
  2258.           else if ((ch&0xFF) == '\n') /* a blank line means end of a para */
  2259.              {
  2260.              *curr++ = '\n';   /* end the para */
  2261.              *curr++ = '\n';   /* for the blank line */
  2262.              in_para = 0;
  2263.              state = S_Start;
  2264.              }
  2265.           else
  2266.              {
  2267.              if (lformat_exclude > 0 && num_spaces >= lformat_exclude )
  2268.                 {
  2269.                 *curr++ = '\n';
  2270.                 in_para = 0;
  2271.                 put_spaces(num_spaces);
  2272.                 num_spaces = 0;
  2273.                 state = S_FormatDisabled;
  2274.                 again = 1;
  2275.                 }
  2276.              else
  2277.                 {
  2278.                 add_blank_for_split();
  2279.                 margin = num_spaces;
  2280.                 *margin_pos = (char)num_spaces;
  2281.                 state = S_Line;
  2282.                 again = 1;
  2283.                 }
  2284.              }
  2285.           break;
  2286.  
  2287.            case S_Line:   /* all lines after the first */
  2288.           if (ch == '\n')
  2289.              {
  2290.              state = S_StartLine;
  2291.              num_spaces = 0;
  2292.              }
  2293.           else if (ch == ('\n' | 0x100) )   /* force end of para ? */
  2294.              {
  2295.              *curr++ = '\n';
  2296.              in_para = 0;
  2297.              state = S_Start;
  2298.              }
  2299.           else if ( ch == ' ' )
  2300.              {
  2301.              state = S_LineSpaces;
  2302.              num_spaces = 1;
  2303.              }
  2304.           else
  2305.              put_a_char(ch, &t);
  2306.           break;
  2307.  
  2308.            case S_LineSpaces:
  2309.           if (ch == ' ')
  2310.              ++num_spaces;
  2311.           else
  2312.              {
  2313.              put_spaces(num_spaces);
  2314.              state = S_Line;
  2315.              again = 1;
  2316.              }
  2317.           break;
  2318.  
  2319.            case S_StartLine:   /* for all lines after the second */
  2320.           if ( ch == ' ')
  2321.              ++num_spaces;
  2322.           else if ((ch&0xFF) == '\n') /* a blank line means end of a para */
  2323.              {
  2324.              *curr++ = '\n';   /* end the para */
  2325.              *curr++ = '\n';   /* for the blank line */
  2326.              in_para = 0;
  2327.              state = S_Start;
  2328.              }
  2329.           else
  2330.              {
  2331.              if ( num_spaces != margin )
  2332.                 {
  2333.                 *curr++ = '\n';
  2334.                 in_para = 0;
  2335.                 state = S_StartFirstLine;  /* with current num_spaces */
  2336.                 again = 1;
  2337.                 }
  2338.              else
  2339.                 {
  2340.                 add_blank_for_split();
  2341.                 state = S_Line;
  2342.                 again = 1;
  2343.                 }
  2344.              }
  2345.           break;
  2346.  
  2347.            case S_FormatDisabled:
  2348.           if ( ch == ' ' )
  2349.              {
  2350.              state = S_FormatDisabledSpaces;
  2351.              num_spaces = 1;
  2352.              }
  2353.           else
  2354.              {
  2355.              if ( (ch&0xFF) == '\n' )
  2356.                 state = S_Start;
  2357.              put_a_char(ch, &t);
  2358.              }
  2359.           break;
  2360.  
  2361.            case S_FormatDisabledSpaces:
  2362.           if ( ch == ' ' )
  2363.              ++num_spaces;
  2364.           else
  2365.              {
  2366.              put_spaces(num_spaces);
  2367.              num_spaces = 0;    /* is this needed? */
  2368.              state = S_FormatDisabled;
  2369.              again = 1;
  2370.              }
  2371.           break;
  2372.  
  2373.            } /* switch (state) */
  2374.         }
  2375.      while (again);
  2376.      }
  2377.  
  2378.       else
  2379.      {
  2380.      do
  2381.         {
  2382.         again = 0;     /* default */
  2383.  
  2384.         switch (state)
  2385.            {
  2386.            case S_Start:
  2387.           if ( ch == ' ' )
  2388.              {
  2389.              state = S_Spaces;
  2390.              num_spaces = 1;
  2391.              }
  2392.           else
  2393.              put_a_char(ch, &t);
  2394.           break;
  2395.  
  2396.            case S_Spaces:
  2397.           if (ch == ' ')
  2398.              ++num_spaces;
  2399.           else
  2400.              {
  2401.              put_spaces(num_spaces);
  2402.              num_spaces = 0;     /* is this needed? */
  2403.              state = S_Start;
  2404.              again = 1;
  2405.              }
  2406.           break;
  2407.            } /* switch */
  2408.         }
  2409.      while (again);
  2410.      }
  2411.  
  2412.       CHK_BUFFER(0)
  2413.       } /* while ( 1 ) */
  2414.  
  2415.    fclose(srcfile);
  2416.  
  2417.    srcline = -1;
  2418.    }
  2419.  
  2420.  
  2421. /*
  2422.  * stuff to resolve hot-link references.
  2423.  */
  2424.  
  2425.  
  2426. void make_hot_links(void)
  2427.    /*
  2428.     * calculate topic_num/topic_off for each link.
  2429.     */
  2430.    {
  2431.    LINK    *l;
  2432.    LABEL   *lbl;
  2433.    int        lctr;
  2434.    int        t;
  2435.    CONTENT *c;
  2436.    int        ctr;
  2437.  
  2438.    msg("Making hot-links.");
  2439.  
  2440.    /*
  2441.     * Calculate topic_num for all entries in DocContents.  Also set
  2442.     * "TF_IN_DOC" flag for all topics included in the document.
  2443.     */
  2444.  
  2445.    for (lctr=0, c=contents; lctr<num_contents; lctr++, c++)
  2446.       {
  2447.       for (ctr=0; ctr<c->num_topic; ctr++)
  2448.      {
  2449.      if ( c->is_label[ctr] )
  2450.         {
  2451.         lbl = find_label(c->topic_name[ctr]);
  2452.         if (lbl == NULL)
  2453.            {
  2454.            src_cfname = c->srcfile;
  2455.            srcline = c->srcline;
  2456.            error(0,"Cannot find DocContent label \"%s\".", c->topic_name[ctr]);
  2457.            srcline = -1;
  2458.            }
  2459.         else
  2460.            {
  2461.            if ( topic[lbl->topic_num].flags & TF_DATA )
  2462.           {
  2463.           src_cfname = c->srcfile;
  2464.           srcline = c->srcline;
  2465.           error(0,"Label \"%s\" is a data-only topic.", c->topic_name[ctr]);
  2466.           srcline = -1;
  2467.           }
  2468.            else
  2469.           {
  2470.           c->topic_num[ctr] = lbl->topic_num;
  2471.           if ( topic[lbl->topic_num].flags & TF_IN_DOC )
  2472.              warn(0,"Topic \"%s\" appears in document more than once.",
  2473.               topic[lbl->topic_num].title);
  2474.           else
  2475.              topic[lbl->topic_num].flags |= TF_IN_DOC;
  2476.           }
  2477.            }
  2478.  
  2479.         }
  2480.      else
  2481.         {
  2482.         t = find_topic_title(c->topic_name[ctr]);
  2483.  
  2484.         if (t == -1)
  2485.            {
  2486.            src_cfname = c->srcfile;
  2487.            srcline = c->srcline;
  2488.            error(0,"Cannot find DocContent topic \"%s\".", c->topic_name[ctr]);
  2489.            srcline = -1;  /* back to reality */
  2490.            }
  2491.         else
  2492.            {
  2493.            c->topic_num[ctr] = t;
  2494.            if ( topic[t].flags & TF_IN_DOC )
  2495.           warn(0,"Topic \"%s\" appears in document more than once.",
  2496.                topic[t].title);
  2497.            else
  2498.           topic[t].flags |= TF_IN_DOC;
  2499.            }
  2500.         }
  2501.      }
  2502.       }
  2503.  
  2504.    /*
  2505.     * Find topic_num and topic_off for all hot-links.  Also flag all hot-
  2506.     * links which will (probably) appear in the document.
  2507.     */
  2508.  
  2509.    for (lctr=0, l=a_link; lctr<num_link; l++, lctr++)
  2510.       {
  2511.       switch ( l->type )
  2512.      {
  2513.      case 0:      /* name is the title of the topic */
  2514.         t = find_topic_title(l->name);
  2515.         if (t == -1)
  2516.            {
  2517.            src_cfname = l->srcfile;
  2518.            srcline = l->srcline; /* pretend we are still in the source... */
  2519.            error(0,"Cannot find implicit hot-link \"%s\".", l->name);
  2520.            srcline = -1;  /* back to reality */
  2521.            }
  2522.         else
  2523.            {
  2524.            l->topic_num = t;
  2525.            l->topic_off = 0;
  2526.            l->doc_page = (topic[t].flags & TF_IN_DOC) ? 0 : -1;
  2527.            }
  2528.         break;
  2529.  
  2530.      case 1:  /* name is the name of a label */
  2531.         lbl = find_label(l->name);
  2532.         if (lbl == NULL)
  2533.            {
  2534.            src_cfname = l->srcfile;
  2535.            srcline = l->srcline; /* pretend again */
  2536.            error(0,"Cannot find explicit hot-link \"%s\".", l->name);
  2537.            srcline = -1;
  2538.            }
  2539.         else
  2540.            {
  2541.            if ( topic[lbl->topic_num].flags & TF_DATA )
  2542.           {
  2543.           src_cfname = l->srcfile;
  2544.           srcline = l->srcline;
  2545.           error(0,"Label \"%s\" is a data-only topic.", l->name);
  2546.           srcline = -1;
  2547.           }
  2548.            else
  2549.           {
  2550.           l->topic_num = lbl->topic_num;
  2551.           l->topic_off = lbl->topic_off;
  2552.           l->doc_page  = (topic[lbl->topic_num].flags & TF_IN_DOC) ? 0 : -1;
  2553.           }
  2554.            }
  2555.         break;
  2556.  
  2557.      case 2:   /* it's a "special" link; topic_off already has the value */
  2558.         break;
  2559.      }
  2560.       }
  2561.  
  2562.    }
  2563.  
  2564.  
  2565. /*
  2566.  * online help pagination stuff
  2567.  */
  2568.  
  2569.  
  2570. void add_page_break(TOPIC *t, int margin, char *text, char *start, char *curr, int num_links)
  2571.    {
  2572.    PAGE p;
  2573.  
  2574.    p.offset = (unsigned) (start - text);
  2575.    p.length = (unsigned) (curr - start);
  2576.    p.margin = margin;
  2577.    add_page(t, &p);
  2578.  
  2579.    if (max_links < num_links)
  2580.       max_links = num_links;
  2581.    }
  2582.  
  2583.  
  2584. void paginate_online(void)    /* paginate the text for on-line help */
  2585.    {               /* also calculates max_pages and max_links */
  2586.    int         lnum;
  2587.    char     *start;
  2588.    char     *curr;
  2589.    char     *text;
  2590.    TOPIC    *t;
  2591.    int         tctr;
  2592.    unsigned  len;
  2593.    int         skip_blanks;
  2594.    int         num_links;
  2595.    int         col;
  2596.    int         tok;
  2597.    int         size,
  2598.          width;
  2599.    int         start_margin;
  2600.  
  2601.    msg("Paginating online help.");
  2602.  
  2603.    for (t=topic, tctr=0; tctr<num_topic; t++, tctr++)
  2604.       {
  2605.       if ( t->flags & TF_DATA )
  2606.      continue;    /* don't paginate data topics */
  2607.  
  2608.       text = get_topic_text(t);
  2609.       curr = text;
  2610.       len  = t->text_len;
  2611.  
  2612.       start = curr;
  2613.       skip_blanks = 0;
  2614.       lnum = 0;
  2615.       num_links = 0;
  2616.       col = 0;
  2617.       start_margin = -1;
  2618.  
  2619.       while (len > 0)
  2620.      {
  2621.      tok = find_token_length(ONLINE, curr, len, &size, &width);
  2622.  
  2623.      switch ( tok )
  2624.         {
  2625.         case TOK_PARA:
  2626.            {
  2627.            int indent,
  2628.            margin;
  2629.  
  2630.            ++curr;
  2631.  
  2632.            indent = *curr++;
  2633.            margin = *curr++;
  2634.  
  2635.            len -= 3;
  2636.  
  2637.            col = indent;
  2638.  
  2639.            while (1)
  2640.           {
  2641.           tok = find_token_length(ONLINE, curr, len, &size, &width);
  2642.  
  2643.           if (tok == TOK_DONE || tok == TOK_NL || tok == TOK_FF )
  2644.              break;
  2645.  
  2646.           if ( tok == TOK_PARA )
  2647.              {
  2648.              col = 0;   /* fake a nl */
  2649.              ++lnum;
  2650.              break;
  2651.              }
  2652.  
  2653.           if (tok == TOK_XONLINE || tok == TOK_XDOC )
  2654.              {
  2655.              curr += size;
  2656.              len -= size;
  2657.              continue;
  2658.              }
  2659.  
  2660.           /* now tok is TOK_SPACE or TOK_LINK or TOK_WORD */
  2661.  
  2662.           if (col+width > SCREEN_WIDTH)
  2663.              {            /* go to next line... */
  2664.              if ( ++lnum >= SCREEN_DEPTH )
  2665.                 {        /* go to next page... */
  2666.                 add_page_break(t, start_margin, text, start, curr, num_links);
  2667.                 start = curr + ( (tok == TOK_SPACE) ? size : 0 );
  2668.                 start_margin = margin;
  2669.                 lnum = 0;
  2670.                 num_links = 0;
  2671.                 }
  2672.              if ( tok == TOK_SPACE )
  2673.                 width = 0;   /* skip spaces at start of a line */
  2674.  
  2675.              col = margin;
  2676.              }
  2677.  
  2678.           col += width;
  2679.           curr += size;
  2680.           len -= size;
  2681.           }
  2682.  
  2683.            skip_blanks = 0;
  2684.            width = size = 0;
  2685.            break;
  2686.            }
  2687.  
  2688.         case TOK_NL:
  2689.            if (skip_blanks && col == 0)
  2690.           {
  2691.           start += size;
  2692.           break;
  2693.           }
  2694.            ++lnum;
  2695.            if ( lnum >= SCREEN_DEPTH || (col == 0 && lnum==SCREEN_DEPTH-1) )
  2696.           {
  2697.           add_page_break(t, start_margin, text, start, curr, num_links);
  2698.           start = curr + size;
  2699.           start_margin = -1;
  2700.           lnum = 0;
  2701.           num_links = 0;
  2702.           skip_blanks = 1;
  2703.           }
  2704.            col = 0;
  2705.            break;
  2706.  
  2707.         case TOK_FF:
  2708.            col = 0;
  2709.            if (skip_blanks)
  2710.           {
  2711.           start += size;
  2712.           break;
  2713.           }
  2714.            add_page_break(t, start_margin, text, start, curr, num_links);
  2715.            start_margin = -1;
  2716.            start = curr + size;
  2717.            lnum = 0;
  2718.            num_links = 0;
  2719.            break;
  2720.  
  2721.         case TOK_DONE:
  2722.         case TOK_XONLINE:   /* skip */
  2723.         case TOK_XDOC:      /* ignore */
  2724.         case TOK_CENTER:    /* ignore */
  2725.            break;
  2726.  
  2727.         case TOK_LINK:
  2728.            ++num_links;
  2729.  
  2730.            /* fall-through */
  2731.  
  2732.         default:    /* TOK_SPACE, TOK_LINK, TOK_WORD */
  2733.            skip_blanks = 0;
  2734.            break;
  2735.  
  2736.         } /* switch */
  2737.  
  2738.      curr += size;
  2739.      len  -= size;
  2740.      col  += width;
  2741.      } /* while */
  2742.  
  2743.       if (!skip_blanks)
  2744.      add_page_break(t, start_margin, text, start, curr, num_links);
  2745.  
  2746.       if (max_pages < t->num_page)
  2747.      max_pages = t->num_page;
  2748.  
  2749.       release_topic_text(t, 0);
  2750.       } /* for */
  2751.    }
  2752.  
  2753.  
  2754. /*
  2755.  * paginate document stuff
  2756.  */
  2757.  
  2758.  
  2759. #define CNUM           0
  2760. #define TNUM           1
  2761. #define LINK_DEST_WARN 2
  2762.  
  2763.  
  2764. typedef struct
  2765.    {
  2766.    int        cnum,  /* must match above #defines so pd_get_info() will work */
  2767.         tnum,
  2768.         link_dest_warn;
  2769.  
  2770.    char far *start;
  2771.    CONTENT  *c;
  2772.    LABEL    *lbl;
  2773.  
  2774.    } PAGINATE_DOC_INFO;
  2775.  
  2776.  
  2777. LABEL *find_next_label_by_topic(int t)
  2778.    {
  2779.    LABEL *temp, *g, *p;
  2780.    int      ctr;
  2781.  
  2782.    g = p = NULL;
  2783.  
  2784.    for (temp=label, ctr=0; ctr<num_label; ctr++, temp++)
  2785.       if ( temp->topic_num == t && temp->doc_page == -1 )
  2786.      {
  2787.      g = temp;
  2788.      break;
  2789.      }
  2790.       else if (temp->topic_num > t)
  2791.      break;
  2792.  
  2793.    for (temp=plabel, ctr=0; ctr<num_plabel; ctr++, temp++)
  2794.       if ( temp->topic_num == t && temp->doc_page == -1 )
  2795.      {
  2796.      p = temp;
  2797.      break;
  2798.      }
  2799.       else if (temp->topic_num > t)
  2800.      break;
  2801.  
  2802.    if ( p == NULL )
  2803.       return (g);
  2804.  
  2805.    else if ( g == NULL )
  2806.       return (p);
  2807.  
  2808.    else
  2809.       return ( (g->topic_off < p->topic_off) ? g : p );
  2810.    }
  2811.  
  2812.  
  2813. void set_hot_link_doc_page(void)
  2814.    /*
  2815.     * Find doc_page for all hot-links.
  2816.     */
  2817.    {
  2818.    LINK  *l;
  2819.    LABEL *lbl;
  2820.    int      lctr;
  2821.    int      t;
  2822.  
  2823.    for (lctr=0, l=a_link; lctr<num_link; l++, lctr++)
  2824.       {
  2825.       switch ( l->type )
  2826.      {
  2827.      case 0:      /* name is the title of the topic */
  2828.         t = find_topic_title(l->name);
  2829.         if (t == -1)
  2830.            {
  2831.            src_cfname = l->srcfile;
  2832.            srcline = l->srcline; /* pretend we are still in the source... */
  2833.            error(0,"Cannot find implicit hot-link \"%s\".", l->name);
  2834.            srcline = -1;  /* back to reality */
  2835.            }
  2836.         else
  2837.            l->doc_page = topic[t].doc_page;
  2838.         break;
  2839.  
  2840.      case 1:  /* name is the name of a label */
  2841.         lbl = find_label(l->name);
  2842.         if (lbl == NULL)
  2843.            {
  2844.            src_cfname = l->srcfile;
  2845.            srcline = l->srcline; /* pretend again */
  2846.            error(0,"Cannot find explicit hot-link \"%s\".", l->name);
  2847.            srcline = -1;
  2848.            }
  2849.         else
  2850.            l->doc_page = lbl->doc_page;
  2851.         break;
  2852.  
  2853.      case 2:   /* special topics don't appear in the document */
  2854.         break;
  2855.      }
  2856.       }
  2857.    }
  2858.  
  2859.  
  2860. void set_content_doc_page(void)
  2861.    /*
  2862.     * insert page #'s in the DocContents
  2863.     */
  2864.    {
  2865.    CONTENT *c;
  2866.    TOPIC   *t;
  2867.    char    *base;
  2868.    int        tnum;
  2869.    int        ctr;
  2870.    char     buf[4];
  2871.    int        len;
  2872.  
  2873.    tnum = find_topic_title(DOCCONTENTS_TITLE);
  2874.    assert(tnum>=0);
  2875.    t = &topic[tnum];
  2876.  
  2877.    base = get_topic_text(t);
  2878.  
  2879.    for (ctr=1, c=contents+1; ctr<num_contents; ctr++, c++)
  2880.       {
  2881.       assert(c->doc_page>=1);
  2882.       sprintf(buf, "%d", c->doc_page);
  2883.       len = strlen(buf);
  2884.       assert(len<=3);
  2885.       memcpy(base+c->page_num_pos+(3-len), buf, len);
  2886.       }
  2887.  
  2888.    release_topic_text(t, 1);
  2889.    }
  2890.  
  2891.  
  2892. int pd_get_info(int cmd, PD_INFO *pd, int *info)
  2893.    {         /* this funtion also used by print_document() */
  2894.    CONTENT *c;
  2895.  
  2896.    switch (cmd)
  2897.       {
  2898.       case PD_GET_CONTENT:
  2899.      if ( ++info[CNUM] >= num_contents )
  2900.         return (0);
  2901.      c = &contents[info[CNUM]];
  2902.      info[TNUM] = -1;
  2903.      pd->id       = c->id;
  2904.      pd->title    = c->name;
  2905.      pd->new_page = (c->flags & CF_NEW_PAGE) ? 1 : 0;
  2906.      return (1);
  2907.  
  2908.       case PD_GET_TOPIC:
  2909.      c = &contents[info[CNUM]];
  2910.      if ( ++info[TNUM] >= c->num_topic )
  2911.         return (0);
  2912.      pd->curr = get_topic_text( &topic[c->topic_num[info[TNUM]]] );
  2913.      pd->len = topic[c->topic_num[info[TNUM]]].text_len;
  2914.      return (1);
  2915.  
  2916.       case PD_GET_LINK_PAGE:
  2917.          if ( a_link[getint(pd->s)].doc_page == -1 )
  2918.         {
  2919.         if ( info[LINK_DEST_WARN] )
  2920.            {
  2921.                src_cfname = a_link[getint(pd->s)].srcfile;
  2922.                srcline    = a_link[getint(pd->s)].srcline;
  2923.            warn(0,"Hot-link destination is not in the document.");
  2924.            srcline = -1;
  2925.            }
  2926.         return (0);
  2927.         }
  2928.          pd->i = a_link[getint(pd->s)].doc_page;
  2929.      return (1);
  2930.  
  2931.       case PD_RELEASE_TOPIC:
  2932.      c = &contents[info[CNUM]];
  2933.      release_topic_text(&topic[c->topic_num[info[TNUM]]], 0);
  2934.      return (1);
  2935.  
  2936.       default:
  2937.      return (0);
  2938.       }
  2939.    }
  2940.  
  2941.  
  2942. int paginate_doc_output(int cmd, PD_INFO *pd, PAGINATE_DOC_INFO *info)
  2943.    {
  2944.    switch (cmd)
  2945.       {
  2946.       case PD_FOOTING:
  2947.       case PD_PRINT:
  2948.       case PD_PRINTN:
  2949.       case PD_PRINT_SEC:
  2950.      return (1);
  2951.  
  2952.       case PD_HEADING:
  2953.      ++num_doc_pages;
  2954.      return (1);
  2955.  
  2956.       case PD_START_SECTION:
  2957.      info->c = &contents[info->cnum];
  2958.      return (1);
  2959.  
  2960.       case PD_START_TOPIC:
  2961.      info->start = pd->curr;
  2962.      info->lbl = find_next_label_by_topic(info->c->topic_num[info->tnum]);
  2963.      return (1);
  2964.  
  2965.       case PD_SET_SECTION_PAGE:
  2966.      info->c->doc_page = pd->pnum;
  2967.      return (1);
  2968.  
  2969.       case PD_SET_TOPIC_PAGE:
  2970.      topic[info->c->topic_num[info->tnum]].doc_page = pd->pnum;
  2971.      return (1);
  2972.  
  2973.       case PD_PERIODIC:
  2974.      while ( info->lbl != NULL && (unsigned)(pd->curr - info->start) >= info->lbl->topic_off)
  2975.         {
  2976.         info->lbl->doc_page = pd->pnum;
  2977.         info->lbl = find_next_label_by_topic(info->c->topic_num[info->tnum]);
  2978.         }
  2979.      return (1);
  2980.  
  2981.       default:
  2982.      return (0);
  2983.       }
  2984.    }
  2985.  
  2986.  
  2987. void paginate_document(void)
  2988.    {
  2989.    PAGINATE_DOC_INFO info;
  2990.  
  2991.    if (num_contents == 0)
  2992.       return ;
  2993.  
  2994.    msg("Paginating document.");
  2995.  
  2996.    info.cnum = info.tnum = -1;
  2997.    info.link_dest_warn = 1;
  2998.  
  2999.    process_document((PD_FUNC)pd_get_info, (PD_FUNC)paginate_doc_output, &info);
  3000.  
  3001.    set_hot_link_doc_page();
  3002.    set_content_doc_page();
  3003.    }
  3004.  
  3005.  
  3006. /*
  3007.  * label sorting stuff
  3008.  */
  3009.  
  3010.  
  3011. int fcmp_LABEL(VOIDCONSTPTR a, VOIDCONSTPTR b)
  3012.    {
  3013.    char *an = ((LABEL *)a)->name,
  3014.         *bn = ((LABEL *)b)->name;
  3015.    int     diff;
  3016.  
  3017.    /* compare the names, making sure that the index goes first */
  3018.  
  3019.    if ( (diff=strcmp(an,bn)) == 0 )
  3020.       return (0);
  3021.  
  3022.    if ( strcmp(an, INDEX_LABEL) == 0 )
  3023.       return (-1);
  3024.  
  3025.    if ( strcmp(bn, INDEX_LABEL) == 0 )
  3026.       return (1);
  3027.  
  3028.    return ( diff );
  3029.    }
  3030.  
  3031.  
  3032. void sort_labels(void)
  3033.    {
  3034.    qsort(label,  num_label,  sizeof(LABEL), fcmp_LABEL);
  3035.    qsort(plabel, num_plabel, sizeof(LABEL), fcmp_LABEL);
  3036.    }
  3037.  
  3038.  
  3039. /*
  3040.  * file write stuff.
  3041.  */
  3042.  
  3043.  
  3044. int compare_files(FILE *f1, FILE *f2) /* returns TRUE if different */
  3045.    {
  3046.    if ( filelength(fileno(f1)) != filelength(fileno(f2)) )
  3047.       return (1);   /* different if sizes are not the same */
  3048.  
  3049.    while ( !feof(f1) && !feof(f2) )
  3050.       if ( getc(f1) != getc(f2) )
  3051.      return (1);
  3052.  
  3053.    return ( ( feof(f1) && feof(f2) ) ? 0 : 1);
  3054.    }
  3055.  
  3056.  
  3057. void _write_hdr(char *fname, FILE *file)
  3058.    {
  3059.    int ctr;
  3060.    char nfile[MAXFILE],
  3061.         next[MAXEXT];
  3062.  
  3063.    FNSPLIT(fname, NULL, NULL, nfile, next);
  3064.    fprintf(file, "\n/*\n * %s%s\n", nfile, next);
  3065.    FNSPLIT(src_fname, NULL, NULL, nfile, next);
  3066.    fprintf(file, " *\n * Contains #defines for help.\n *\n");
  3067.    fprintf(file, " * Generated by HC from: %s%s\n *\n */\n\n\n", nfile, next);
  3068.  
  3069.    fprintf(file, "/* current help file version */\n");
  3070.    fprintf(file, "\n");
  3071.    fprintf(file, "#define %-32s %3d\n", "HELP_VERSION", version);
  3072.    fprintf(file, "\n\n");
  3073.  
  3074.    fprintf(file, "/* labels */\n\n");
  3075.  
  3076.    for (ctr=0; ctr<num_label; ctr++)
  3077.       if (label[ctr].name[0] != '@')  /* if it's not a local label... */
  3078.      {
  3079.      fprintf(file, "#define %-32s %3d", label[ctr].name, ctr);
  3080.      if ( strcmp(label[ctr].name, INDEX_LABEL) == 0 )
  3081.         fprintf(file, "        /* index */");
  3082.      fprintf(file, "\n");
  3083.      }
  3084.  
  3085.    fprintf(file, "\n\n");
  3086.    }
  3087.  
  3088.  
  3089. void write_hdr(char *fname)
  3090.    {
  3091.    FILE *temp,
  3092.         *hdr;
  3093.  
  3094.    hdr = fopen(fname, "rt");
  3095.  
  3096.    if (hdr == NULL)
  3097.       {         /* if no prev. hdr file generate a new one */
  3098.       hdr = fopen(fname, "wt");
  3099.       if (hdr == NULL)
  3100.      fatal(0,"Cannot create \"%s\".", fname);
  3101.       msg("Writing: %s", fname);
  3102.       _write_hdr(fname, hdr);
  3103.       fclose(hdr);
  3104.       notice("FRACTINT must be re-compiled.");
  3105.       return ;
  3106.       }
  3107.  
  3108.    msg("Comparing: %s", fname);
  3109.  
  3110.    temp = fopen(TEMP_FNAME, "wt");
  3111.  
  3112.    if (temp == NULL)
  3113.       fatal(0,"Cannot create temporary file: \"%s\".", TEMP_FNAME);
  3114.  
  3115.    _write_hdr(fname, temp);
  3116.  
  3117.    fclose(temp);
  3118.    temp = fopen(TEMP_FNAME, "rt");
  3119.  
  3120.    if (temp == NULL)
  3121.       fatal(0,"Cannot open temporary file: \"%s\".", TEMP_FNAME);
  3122.  
  3123.    if ( compare_files(temp, hdr) )   /* if they are different... */
  3124.       {
  3125.       msg("Updating: %s", fname);
  3126.       fclose(temp);
  3127.       fclose(hdr);
  3128.       unlink(fname);           /* delete the old hdr file */
  3129.       rename(TEMP_FNAME, fname);   /* rename the temp to the hdr file */
  3130.       notice("FRACTINT must be re-compiled.");
  3131.       }
  3132.    else
  3133.       {   /* if they are the same leave the original alone. */
  3134.       fclose(temp);
  3135.       fclose(hdr);
  3136.       unlink(TEMP_FNAME);      /* delete the temp */
  3137.       }
  3138.    }
  3139.  
  3140.  
  3141. void calc_offsets(void)    /* calc file offset to each topic */
  3142.    {
  3143.    int        t;
  3144.    TOPIC   *tp;
  3145.    long     offset;
  3146.    CONTENT *cp;
  3147.    int        c;
  3148.  
  3149.    /* NOTE: offsets do NOT include 6 bytes for signature & version! */
  3150.  
  3151.    offset = sizeof(int) +           /* max_pages */
  3152.             sizeof(int) +           /* max_links */
  3153.             sizeof(int) +           /* num_topic */
  3154.             sizeof(int) +           /* num_label */
  3155.             sizeof(int) +           /* num_contents */
  3156.             sizeof(int) +           /* num_doc_pages */
  3157.             num_topic*sizeof(long) +/* offsets to each topic */
  3158.             num_label*2*sizeof(int);/* topic_num/topic_off for all public labels */
  3159.  
  3160.    for (c=0, cp=contents; c<num_contents; c++, cp++)
  3161.       offset += sizeof(int) +       /* flags */
  3162.             1 +            /* id length */
  3163.             strlen(cp->id) +    /* id text */
  3164.             1 +            /* name length */
  3165.             strlen(cp->name) +  /* name text */
  3166.             1 +            /* number of topics */
  3167.                 cp->num_topic*sizeof(int);    /* topic numbers */
  3168.  
  3169.    for (t=0, tp=topic; t<num_topic; t++, tp++)
  3170.       {
  3171.       tp->offset = offset;
  3172.       offset += (long)sizeof(int) + /* topic flags */
  3173.                 sizeof(int) +       /* number of pages */
  3174.                 tp->num_page*3*sizeof(int) +   /* page offset, length & starting margin */
  3175.             1 +            /* length of title */
  3176.             tp->title_len +     /* title */
  3177.                 sizeof(int) +       /* length of text */
  3178.             tp->text_len;        /* text */
  3179.       }
  3180.  
  3181.    }
  3182.  
  3183.  
  3184. void insert_real_link_info(char *curr, unsigned len)
  3185.    /*
  3186.     * Replaces link indexes in the help text with topic_num, topic_off and
  3187.     * doc_page info.
  3188.     */
  3189.    {
  3190.    int         size;
  3191.    int         tok;
  3192.    LINK     *l;
  3193.  
  3194.    while (len > 0)
  3195.       {
  3196.       tok = find_token_length(0, curr, len, &size, NULL);
  3197.  
  3198.       if ( tok == TOK_LINK )
  3199.      {
  3200.          l = &a_link[ getint(curr+1) ];
  3201.          setint(curr+1,l->topic_num);
  3202.          setint(curr+1+sizeof(int),l->topic_off);
  3203.          setint(curr+1+2*sizeof(int),l->doc_page);
  3204.      }
  3205.  
  3206.       len -= size;
  3207.       curr += size;
  3208.       }
  3209.    }
  3210.  
  3211.  
  3212. void _write_help(FILE *file)
  3213.    {
  3214.    int             t, p, l, c;
  3215.    char             *text;
  3216.    TOPIC            *tp;
  3217.    CONTENT            *cp;
  3218.    struct help_sig_info  hs;
  3219.  
  3220.    /* write the signature and version */
  3221.  
  3222.    hs.sig = HELP_SIG;
  3223.    hs.version = version;
  3224.  
  3225.    fwrite(&hs, sizeof(long)+sizeof(int), 1, file);
  3226.  
  3227.    /* write max_pages & max_links */
  3228.  
  3229.    putw(max_pages, file);
  3230.    putw(max_links, file);
  3231.  
  3232.    /* write num_topic, num_label and num_contents */
  3233.  
  3234.    putw(num_topic, file);
  3235.    putw(num_label, file);
  3236.    putw(num_contents, file);
  3237.  
  3238.    /* write num_doc_page */
  3239.  
  3240.    putw(num_doc_pages, file);
  3241.  
  3242.    /* write the offsets to each topic */
  3243.  
  3244.    for (t=0; t<num_topic; t++)
  3245.       fwrite(&topic[t].offset, sizeof(long), 1, file);
  3246.  
  3247.    /* write all public labels */
  3248.  
  3249.    for (l=0; l<num_label; l++)
  3250.       {
  3251.       putw(label[l].topic_num, file);
  3252.       putw(label[l].topic_off, file);
  3253.       }
  3254.  
  3255.    /* write contents */
  3256.  
  3257.    for (c=0, cp=contents; c<num_contents; c++, cp++)
  3258.       {
  3259.       putw(cp->flags, file);
  3260.  
  3261.       t = strlen(cp->id);
  3262.       putc((BYTE)t, file);
  3263.       fwrite(cp->id, 1, t, file);
  3264.  
  3265.       t = strlen(cp->name);
  3266.       putc((BYTE)t, file);
  3267.       fwrite(cp->name, 1, t, file);
  3268.  
  3269.       putc((BYTE)cp->num_topic, file);
  3270.       fwrite(cp->topic_num, sizeof(int), cp->num_topic, file);
  3271.       }
  3272.  
  3273.    /* write topics */
  3274.  
  3275.    for (t=0, tp=topic; t<num_topic; t++, tp++)
  3276.       {
  3277.       /* write the topics flags */
  3278.  
  3279.       putw(tp->flags, file);
  3280.  
  3281.       /* write offset, length and starting margin for each page */
  3282.  
  3283.       putw(tp->num_page, file);
  3284.       for (p=0; p<tp->num_page; p++)
  3285.      {
  3286.      putw(tp->page[p].offset, file);
  3287.      putw(tp->page[p].length, file);
  3288.      putw(tp->page[p].margin, file);
  3289.      }
  3290.  
  3291.       /* write the help title */
  3292.  
  3293.       putc((BYTE)tp->title_len, file);
  3294.       fwrite(tp->title, 1, tp->title_len, file);
  3295.  
  3296.       /* insert hot-link info & write the help text */
  3297.  
  3298.       text = get_topic_text(tp);
  3299.  
  3300.       if ( !(tp->flags & TF_DATA) )   /* don't process data topics... */
  3301.      insert_real_link_info(text, tp->text_len);
  3302.  
  3303.       putw(tp->text_len, file);
  3304.       fwrite(text, 1, tp->text_len, file);
  3305.  
  3306.       release_topic_text(tp, 0);  /* don't save the text even though        */
  3307.                   /* insert_real_link_info() modified it    */
  3308.                   /* because we don't access the info after */
  3309.                   /* this.                    */
  3310.  
  3311.       }
  3312.    }
  3313.  
  3314.  
  3315. void write_help(char *fname)
  3316.    {
  3317.    FILE *hlp;
  3318.  
  3319.    hlp = fopen(fname, "wb");
  3320.  
  3321.    if (hlp == NULL)
  3322.       fatal(0,"Cannot create .HLP file: \"%s\".", fname);
  3323.  
  3324.    msg("Writing: %s", fname);
  3325.  
  3326.    _write_help(hlp);
  3327.  
  3328.    fclose(hlp);
  3329.    }
  3330.  
  3331.  
  3332. /*
  3333.  * print document stuff.
  3334.  */
  3335.  
  3336.  
  3337. typedef struct
  3338.    {
  3339.  
  3340.    /*
  3341.     * Note: Don't move these first three or pd_get_info will work not
  3342.     *        correctly.
  3343.     */
  3344.  
  3345.    int        cnum;
  3346.    int        tnum;
  3347.    int        link_dest_warn;   /* = 0 */
  3348.  
  3349.    FILE    *file;
  3350.    int        margin;
  3351.    int        start_of_line;
  3352.    int        spaces;
  3353.    } PRINT_DOC_INFO;
  3354.  
  3355.  
  3356. void printerc(PRINT_DOC_INFO *info, int c, int n)
  3357.    {
  3358.    while ( n-- > 0 )
  3359.       {
  3360.       if (c==' ')
  3361.      ++info->spaces;
  3362.  
  3363.       else if (c=='\n' || c=='\f')
  3364.      {
  3365.      info->start_of_line = 1;
  3366.      info->spaces = 0;   /* strip spaces before a new-line */
  3367.      putc(c, info->file);
  3368.      }
  3369.  
  3370.       else
  3371.      {
  3372.      if (info->start_of_line)
  3373.         {
  3374.         info->spaces += info->margin;
  3375.         info->start_of_line = 0;
  3376.         }
  3377.  
  3378.      while (info->spaces > 0)
  3379.         {
  3380.         fputc(' ', info->file);
  3381.         --info->spaces;
  3382.         }
  3383.  
  3384.      fputc(c, info->file);
  3385.      }
  3386.       }
  3387.    }
  3388.  
  3389.  
  3390. void printers(PRINT_DOC_INFO *info, char far *s, int n)
  3391.    {
  3392.    if (n > 0)
  3393.       {
  3394.       while ( n-- > 0 )
  3395.      printerc(info, *s++, 1);
  3396.       }
  3397.    else
  3398.       {
  3399.       while ( *s != '\0' )
  3400.      printerc(info, *s++, 1);
  3401.       }
  3402.    }
  3403.  
  3404.  
  3405. int print_doc_output(int cmd, PD_INFO *pd, PRINT_DOC_INFO *info)
  3406.    {
  3407.    switch (cmd)
  3408.       {
  3409.       case PD_HEADING:
  3410.      {
  3411.      char buff[20];
  3412.  
  3413.      info->margin = 0;
  3414.      printers(info, "\n                     Fractint Version xx.xx                     Page ", 0);
  3415.      sprintf(buff, "%d\n\n", pd->pnum);
  3416.      printers(info, buff, 0);
  3417.      info->margin = PAGE_INDENT;
  3418.      return (1);
  3419.      }
  3420.  
  3421.       case PD_FOOTING:
  3422.      info->margin = 0;
  3423.      printerc(info, '\f', 1);
  3424.      info->margin = PAGE_INDENT;
  3425.      return (1);
  3426.  
  3427.       case PD_PRINT:
  3428.      printers(info, pd->s, pd->i);
  3429.      return (1);
  3430.  
  3431.       case PD_PRINTN:
  3432.      printerc(info, *pd->s, pd->i);
  3433.      return (1);
  3434.  
  3435.       case PD_PRINT_SEC:
  3436.      info->margin = TITLE_INDENT;
  3437.      if (pd->id[0] != '\0')
  3438.         {
  3439.         printers(info, pd->id, 0);
  3440.         printerc(info, ' ', 1);
  3441.         }
  3442.      printers(info, pd->title, 0);
  3443.      printerc(info, '\n', 1);
  3444.      info->margin = PAGE_INDENT;
  3445.      return (1);
  3446.  
  3447.       case PD_START_SECTION:
  3448.       case PD_START_TOPIC:
  3449.       case PD_SET_SECTION_PAGE:
  3450.       case PD_SET_TOPIC_PAGE:
  3451.       case PD_PERIODIC:
  3452.      return (1);
  3453.  
  3454.       default:
  3455.      return (0);
  3456.       }
  3457.    }
  3458.  
  3459.  
  3460. void print_document(char *fname)
  3461.    {
  3462.    PRINT_DOC_INFO info;
  3463.  
  3464.    if (num_contents == 0)
  3465.       fatal(0,".SRC has no DocContents.");
  3466.  
  3467.    msg("Printing to: %s", fname);
  3468.  
  3469.    info.cnum = info.tnum = -1;
  3470.    info.link_dest_warn = 0;
  3471.  
  3472.    if ( (info.file = fopen(fname, "wt")) == NULL )
  3473.       fatal(0,"Couldn't create \"%s\"", fname);
  3474.  
  3475.    info.margin = PAGE_INDENT;
  3476.    info.start_of_line = 1;
  3477.    info.spaces = 0;
  3478.  
  3479.    process_document((PD_FUNC)pd_get_info, (PD_FUNC)print_doc_output, &info);
  3480.  
  3481.    fclose(info.file);
  3482.    }
  3483.  
  3484.  
  3485. /*
  3486.  * compiler status and memory usage report stuff.
  3487.  */
  3488.  
  3489.  
  3490. void report_memory(void)
  3491.    {
  3492.    long string = 0,   /* bytes in strings */
  3493.         text   = 0,   /* bytes in topic text (stored on disk) */
  3494.         data   = 0,   /* bytes in active data structure */
  3495.         dead   = 0;   /* bytes in unused data structure */
  3496.    int  ctr, ctr2;
  3497.  
  3498.    for (ctr=0; ctr<num_topic; ctr++)
  3499.       {
  3500.       data   += sizeof(TOPIC);
  3501.       string += topic[ctr].title_len;
  3502.       text   += topic[ctr].text_len;
  3503.       data   += topic[ctr].num_page * sizeof(PAGE);
  3504.  
  3505.       dead   += (PAGE_ALLOC_SIZE-(topic[ctr].num_page%PAGE_ALLOC_SIZE)) * sizeof(PAGE);
  3506.       }
  3507.  
  3508.    for (ctr=0; ctr<num_link; ctr++)
  3509.       {
  3510.       data += sizeof(LINK);
  3511.       string += strlen(a_link[ctr].name);
  3512.       }
  3513.  
  3514.    if (num_link > 0)
  3515.       dead += (LINK_ALLOC_SIZE-(num_link%LINK_ALLOC_SIZE)) * sizeof(LINK);
  3516.  
  3517.    for (ctr=0; ctr<num_label; ctr++)
  3518.       {
  3519.       data   += sizeof(LABEL);
  3520.       string += strlen(label[ctr].name) + 1;
  3521.       }
  3522.  
  3523.    if (num_label > 0)
  3524.       dead += (LABEL_ALLOC_SIZE-(num_label%LABEL_ALLOC_SIZE)) * sizeof(LABEL);
  3525.  
  3526.    for (ctr=0; ctr<num_plabel; ctr++)
  3527.       {
  3528.       data   += sizeof(LABEL);
  3529.       string += strlen(plabel[ctr].name) + 1;
  3530.       }
  3531.  
  3532.    if (num_plabel > 0)
  3533.       dead += (LABEL_ALLOC_SIZE-(num_plabel%LABEL_ALLOC_SIZE)) * sizeof(LABEL);
  3534.  
  3535.    for (ctr=0; ctr<num_contents; ctr++)
  3536.       {
  3537.       int t;
  3538.  
  3539.       t = ( MAX_CONTENT_TOPIC - contents[ctr].num_topic ) *
  3540.       ( sizeof(contents[0].is_label[0])   +
  3541.         sizeof(contents[0].topic_name[0]) +
  3542.         sizeof(contents[0].topic_num[0])     );
  3543.       data += sizeof(CONTENT) - t;
  3544.       dead += t;
  3545.       string += strlen(contents[ctr].id) + 1;
  3546.       string += strlen(contents[ctr].name) + 1;
  3547.       for (ctr2=0; ctr2<contents[ctr].num_topic; ctr2++)
  3548.      string += strlen(contents[ctr].topic_name[ctr2]) + 1;
  3549.       }
  3550.  
  3551.    dead += (CONTENTS_ALLOC_SIZE-(num_contents%CONTENTS_ALLOC_SIZE)) * sizeof(CONTENT);
  3552.  
  3553.    printf("\n");
  3554.    printf("Memory Usage:\n");
  3555.    printf("%8ld Bytes in buffers.\n", (long)BUFFER_SIZE);
  3556.    printf("%8ld Bytes in strings.\n", string);
  3557.    printf("%8ld Bytes in data.\n", data);
  3558.    printf("%8ld Bytes in dead space.\n", dead);
  3559.    printf("--------\n");
  3560.    printf("%8ld Bytes total.\n", (long)BUFFER_SIZE+string+data+dead);
  3561.    printf("\n");
  3562.    printf("Disk Usage:\n");
  3563.    printf("%8ld Bytes in topic text.\n", text);
  3564.    }
  3565.  
  3566.  
  3567. void report_stats(void)
  3568.    {
  3569.    int  pages = 0;
  3570.    int        t;
  3571.  
  3572.    for (t=0; t<num_topic; t++)
  3573.       pages += topic[t].num_page;
  3574.  
  3575.    printf("\n");
  3576.    printf("Statistics:\n");
  3577.    printf("%8d Topics\n", num_topic);
  3578.    printf("%8d Links\n", num_link);
  3579.    printf("%8d Labels\n", num_label);
  3580.    printf("%8d Private labels\n", num_plabel);
  3581.    printf("%8d Table of contents (DocContent) entries\n", num_contents);
  3582.    printf("%8d Online help pages\n", pages);
  3583.    printf("%8d Document pages\n", num_doc_pages);
  3584.    }
  3585.  
  3586.  
  3587. /*
  3588.  * add/delete help from .EXE functions.
  3589.  */
  3590.  
  3591.  
  3592. void add_hlp_to_exe(char *hlp_fname, char *exe_fname)
  3593.    {
  3594.    int                exe,   /* handles */
  3595.                 hlp;
  3596.    long             len,
  3597.                 count;
  3598.    int                size;
  3599.    struct help_sig_info hs;
  3600.  
  3601.    if ( (exe=open(exe_fname, O_RDWR|O_BINARY)) == -1 )
  3602.       fatal(0,"Unable to open \"%s\"", exe_fname);
  3603.  
  3604.    if ( (hlp=open(hlp_fname, O_RDONLY|O_BINARY)) == -1 )
  3605.       fatal(0,"Unable to open \"%s\"", hlp_fname);
  3606.  
  3607.    msg("Appending %s to %s", hlp_fname, exe_fname);
  3608.  
  3609.    /* first, check and see if any help is currently installed */
  3610.  
  3611.    lseek(exe, filelength(exe) - sizeof(struct help_sig_info), SEEK_SET);
  3612.  
  3613.    read(exe, (char *)&hs, 10);
  3614.  
  3615.    if ( hs.sig == HELP_SIG )
  3616.       warn(0,"Overwriting previous help. (Version=%d)", hs.version);
  3617.    else
  3618.       hs.base = filelength(exe);
  3619.  
  3620.    /* now, let's see if their help file is for real (and get the version) */
  3621.  
  3622.    read(hlp, (char *)&hs, sizeof(long)+sizeof(int));
  3623.  
  3624.    if (hs.sig != HELP_SIG )
  3625.       fatal(0,"Help signature not found in %s", hlp_fname);
  3626.  
  3627.    msg("Help file %s Version=%d", hlp_fname, hs.version);
  3628.  
  3629.    /* append the help stuff, overwriting old help (if any) */
  3630.  
  3631.    lseek(exe, hs.base, SEEK_SET);
  3632.  
  3633.    len = filelength(hlp) - sizeof(long) - sizeof(int); /* adjust for the file signature & version */
  3634.  
  3635.    for (count=0; count<len; )
  3636.       {
  3637.       size = (int) min((long)BUFFER_SIZE, len-count);
  3638.       read(hlp, buffer, size);
  3639.       write(exe, buffer, size);
  3640.       count += size;
  3641.       }
  3642.  
  3643.    /* add on the signature, version and offset */
  3644.  
  3645.    write(exe, (char *)&hs, 10);
  3646.  
  3647.    chsize(exe, tell(exe));   /* truncate in case old help was longer */
  3648.  
  3649.    close(exe);
  3650.    close(hlp);
  3651.    }
  3652.  
  3653.  
  3654. void delete_hlp_from_exe(char *exe_fname)
  3655.    {
  3656.    int     exe;   /* file handle */
  3657.    struct help_sig_info hs;
  3658.  
  3659.    if ( (exe=open(exe_fname, O_RDWR|O_BINARY)) == -1 )
  3660.       fatal(0,"Unable to open \"%s\"", exe_fname);
  3661.  
  3662.    msg("Deleting help from %s", exe_fname);
  3663.  
  3664.    /* see if any help is currently installed */
  3665.  
  3666. #ifndef XFRACT
  3667.    lseek(exe, filelength(exe) - 10, SEEK_SET);
  3668.    read(exe, (char *)&hs, 10);
  3669. #else
  3670.    lseek(exe, filelength(exe) - 12, SEEK_SET);
  3671.    read(exe, (char *)&hs, 12);
  3672. #endif
  3673.  
  3674.    if ( hs.sig == HELP_SIG )
  3675.       {
  3676.       chsize(exe, hs.base);   /* truncate at the start of the help */
  3677.       close(exe);
  3678.       }
  3679.    else
  3680.       {
  3681.       close(exe);
  3682.       fatal(0,"No help found in %s", exe_fname);
  3683.       }
  3684.    }
  3685.  
  3686.  
  3687. /*
  3688.  * command-line parser, etc.
  3689.  */
  3690.  
  3691.  
  3692. #define MODE_COMPILE 1
  3693. #define MODE_PRINT   2
  3694. #define MODE_APPEND  3
  3695. #define MODE_DELETE  4
  3696.  
  3697.  
  3698. int main(int argc, char *argv[])
  3699.    {
  3700.    int      show_stats = 0,
  3701.       show_mem   = 0;
  3702.    int      mode         = 0;
  3703.  
  3704.    char **arg;
  3705.    char   fname1[81],
  3706.       fname2[81];
  3707.    char   swappath[81];
  3708.  
  3709.    fname1[0] = fname2[0] = swappath[0] = 0;
  3710.  
  3711.    printf("HC - FRACTINT Help Compiler.\n\n");
  3712.  
  3713.    buffer = malloc(BUFFER_SIZE);
  3714.  
  3715.    if (buffer == NULL)
  3716.       fatal(0,"Not enough memory to allocate buffer.");
  3717.  
  3718.    for (arg=&argv[1]; argc>1; argc--, arg++)
  3719.       {
  3720.       switch ( (*arg)[0] )
  3721.      {
  3722.      case '/':
  3723.      case '-':
  3724.         switch ( (*arg)[1] )
  3725.            {
  3726.            case 'c':
  3727.           if (mode == 0)
  3728.              mode = MODE_COMPILE;
  3729.           else
  3730.              fatal(0,"Cannot have /c with /a, /d or /p");
  3731.           break;
  3732.  
  3733.            case 'a':
  3734.           if (mode == 0)
  3735.              mode = MODE_APPEND;
  3736.           else
  3737.              fatal(0,"Cannot have /a with /c, /d or /p");
  3738.           break;
  3739.  
  3740.            case 'd':
  3741.           if (mode == 0)
  3742.              mode = MODE_DELETE;
  3743.           else
  3744.              fatal(0,"Cannot have /d with /c, /a or /p");
  3745.           break;
  3746.  
  3747.            case 'p':
  3748.           if (mode == 0)
  3749.              mode = MODE_PRINT;
  3750.           else
  3751.              fatal(0,"Cannot have /p with /c, /a or /d");
  3752.           break;
  3753.  
  3754.            case 'm':
  3755.           if (mode == MODE_COMPILE)
  3756.              show_mem = 1;
  3757.           else
  3758.              fatal(0,"/m switch allowed only when compiling (/c)");
  3759.           break;
  3760.  
  3761.            case 's':
  3762.           if (mode == MODE_COMPILE)
  3763.              show_stats = 1;
  3764.           else
  3765.              fatal(0,"/s switch allowed only when compiling (/c)");
  3766.           break;
  3767.  
  3768.            case 'r':
  3769.           if (mode == MODE_COMPILE || mode == MODE_PRINT)
  3770.              strcpy(swappath, (*arg)+2);
  3771.           else
  3772.              fatal(0,"/r switch allowed when compiling (/c) or printing (/p)");
  3773.           break;
  3774.  
  3775.            case 'q':
  3776.           quiet_mode = 1;
  3777.           break;
  3778.  
  3779.            default:
  3780.           fatal(0,"Bad command-line switch /%c", (*arg)[1]);
  3781.           break;
  3782.            }
  3783.         break;
  3784.  
  3785.      default:   /* assume it is a fname */
  3786.         if (fname1[0] == '\0')
  3787.            strcpy(fname1, *arg);
  3788.         else if (fname2[0] == '\0')
  3789.            strcpy(fname2, *arg);
  3790.         else
  3791.            fatal(0,"Unexpected command-line argument \"%s\"", *arg);
  3792.         break;
  3793.      } /* switch */
  3794.       } /* for */
  3795.  
  3796.    strupr(fname1);
  3797.    strupr(fname2);
  3798.    strupr(swappath);
  3799.  
  3800.    switch (mode)
  3801.       {
  3802.       case 0:
  3803.          printf( "To compile a .SRC file:\n");
  3804.          printf( "      HC /c [/s] [/m] [/r[path]] [src_file]\n");
  3805.          printf( "         /s       = report statistics.\n");
  3806.          printf( "         /m       = report memory usage.\n");
  3807.          printf( "         /r[path] = set swap file path.\n");
  3808.          printf( "         src_file = .SRC file.  Default is \"%s\"\n", DEFAULT_SRC_FNAME);
  3809.          printf( "To print a .SRC file:\n");
  3810.          printf( "      HC /p [/r[path]] [src_file] [out_file]\n");
  3811.          printf( "         /r[path] = set swap file path.\n");
  3812.          printf( "         src_file = .SRC file.  Default is \"%s\"\n", DEFAULT_SRC_FNAME);
  3813.          printf( "         out_file = Filename to print to. Default is \"%s\"\n",
  3814.          DEFAULT_DOC_FNAME);
  3815.          printf( "To append a .HLP file to an .EXE file:\n");
  3816.          printf( "      HC /a [hlp_file] [exe_file]\n");
  3817.          printf( "         hlp_file = .HLP file.  Default is \"%s\"\n", DEFAULT_HLP_FNAME);
  3818.          printf( "         exe_file = .EXE file.  Default is \"%s\"\n", DEFAULT_EXE_FNAME);
  3819.          printf( "To delete help info from an .EXE file:\n");
  3820.          printf( "      HC /d [exe_file]\n");
  3821.          printf( "         exe_file = .EXE file.  Default is \"%s\"\n", DEFAULT_EXE_FNAME);
  3822.          printf( "\n");
  3823.          printf( "Use \"/q\" for quiet mode. (No status messages.)\n");
  3824.      break;
  3825.  
  3826.       case MODE_COMPILE:
  3827.      if (fname2[0] != '\0')
  3828.         fatal(0,"Unexpected command-line argument \"%s\"", fname2);
  3829.  
  3830.      strcpy(src_fname, (fname1[0]=='\0') ? DEFAULT_SRC_FNAME : fname1);
  3831.  
  3832.      strcat(swappath, SWAP_FNAME);
  3833.  
  3834.      if ( (swapfile=fopen(swappath, "w+b")) == NULL )
  3835.         fatal(0,"Cannot create swap file \"%s\"", swappath);
  3836.      swappos = 0;
  3837.  
  3838.      read_src(src_fname);
  3839.  
  3840.      if (hdr_fname[0] == '\0')
  3841.         error(0,"No .H file defined.  (Use \"~HdrFile=\")");
  3842.      if (hlp_fname[0] == '\0')
  3843.         error(0,"No .HLP file defined.  (Use \"~HlpFile=\")");
  3844.      if (version == -1)
  3845.         warn(0,"No help version has been defined.  (Use \"~Version=\")");
  3846.  
  3847.      /* order of these is very important... */
  3848.  
  3849.      make_hot_links();  /* do even if errors since it may report */
  3850.                 /* more... */
  3851.  
  3852.      if ( !errors )     paginate_online();
  3853.      if ( !errors )     paginate_document();
  3854.      if ( !errors )     calc_offsets();
  3855.      if ( !errors )     sort_labels();
  3856.      if ( !errors )     write_hdr(hdr_fname);
  3857.      if ( !errors )     write_help(hlp_fname);
  3858.  
  3859.      if ( show_stats )
  3860.         report_stats();
  3861.  
  3862.      if ( show_mem )
  3863.         report_memory();
  3864.  
  3865.      if ( errors || warnings )
  3866.         report_errors();
  3867.  
  3868.      fclose(swapfile);
  3869.      remove(swappath);
  3870.  
  3871.      break;
  3872.  
  3873.       case MODE_PRINT:
  3874.      strcpy(src_fname, (fname1[0]=='\0') ? DEFAULT_SRC_FNAME : fname1);
  3875.  
  3876.      strcat(swappath, SWAP_FNAME);
  3877.  
  3878.      if ( (swapfile=fopen(swappath, "w+b")) == NULL )
  3879.         fatal(0,"Cannot create swap file \"%s\"", swappath);
  3880.      swappos = 0;
  3881.  
  3882.      read_src(src_fname);
  3883.  
  3884.      make_hot_links();
  3885.  
  3886.      if ( !errors )     paginate_document();
  3887.      if ( !errors )     print_document( (fname2[0]=='\0') ? DEFAULT_DOC_FNAME : fname2 );
  3888.  
  3889.      if ( errors || warnings )
  3890.         report_errors();
  3891.  
  3892.      fclose(swapfile);
  3893.      remove(swappath);
  3894.  
  3895.      break;
  3896.  
  3897.       case MODE_APPEND:
  3898.      add_hlp_to_exe( (fname1[0]=='\0') ? DEFAULT_HLP_FNAME : fname1,
  3899.              (fname2[0]=='\0') ? DEFAULT_EXE_FNAME : fname2);
  3900.      break;
  3901.  
  3902.       case MODE_DELETE:
  3903.      if (fname2[0] != '\0')
  3904.         fatal(0,"Unexpected argument \"%s\"", fname2);
  3905.      delete_hlp_from_exe((fname1[0]=='\0') ? DEFAULT_EXE_FNAME : fname1);
  3906.      break;
  3907.       }
  3908.  
  3909.    free(buffer);
  3910.  
  3911.    return ( errors );   /* return the number of errors */
  3912.    }
  3913.  
  3914.  
  3915.