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