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