Warning fix (Andrew Stanaski).
[nedit.git] / doc / setext
blobdc48365cba3d0a3208e1b7b80c1720086aa94e1d
1 eval 'exec perl -S $0 ${1+"$@"}'
2     if $running_under_some_shell;
4 #-----------------------------------------------------------------------------
6 # setext.pl -- Structure Enhanced Text Converter (to HTML or simple text)
7
8 # $Id: setext,v 1.11 2003/11/22 13:03:38 edg Exp $
9
10 # Copyright (c) 2000 Steven Haehn
11
12 # This is free software; you can redistribute it and/or modify it under the   
13 # terms of the GNU General Public License as published by the Free Software   
14 # Foundation; either version 2 of the License, or (at your option) any later  
15 # version.                                                                    
16 #                                                                             
17 # This software is distributed in the hope that it will be useful, but WITHOUT
18 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       
19 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
20 # for more details.                                                           
21 #                                                                             
22 # You should have received a copy of the GNU General Public License along with
23 # software; if not, write to the Free Software Foundation, Inc., 59 Temple    
24 # Place, Suite 330, Boston, MA  02111-1307 USA                                
25
26 #-----------------------------------------------------------------------------
28 # The concept of setext documents is the brain child of Ian Feldman.
29 # Some typotag terms used herein were originally implemented in a perl script
30 # by Tony Sanders, which is the inspirational source for this work.
31 # This perl script understands the original typotags, plus extras needed for 
32 # hypertext links, conditional text, and variables.
34 # Samples of setext documents are regularly provided to those folks which
35 # receive the TidBITS publication from www.tidbits.com in their e-mail.
37 # This program is really two programs crammed into one file. The two separate
38 # pieces share lots of code. Instead of having 3 separate files, one of
39 # them being a perl library with the shared code, there was a desire to keep
40 # everything rolled up in one suitcase.
42 #-----------------------------------------------------------------------------
44 # GENERAL TRANSLATOR ROUTINES    NEDIT HELP SOURCE CODE GENERATION ROUTINES
46 # check_target_reference         collect_internal_hypertext_references
47 # count                          emit_copyright
48 # date                           emit_helpText
49 # emit_paragraph                 emit_helpTitles
50 # emit_setext_definition         emit_help_header
51 # extract_fields                 emit_help_label
52 # extract_menu_info              emit_help_menu
53 # extract_menu_init              emit_help_menu_text
54 # get_menu_item                  emit_help_topic
55 # get_setext                     get_menu_text
56 # is_member                      get_newline
57 # parse_setext                   get_style
58 # preserve_html                  get_style_name
59 # recover_extractions            is_known_link
60 # replace_underlines             locate_menu_text
61 # show_usage                     make_NEdit_menu_code
62 # to_state                       print_menu
63 # translate_setext               
64 #                                
65 #                                
66 # TYPOTAG TRANSLATION ROUTINES
67
68 # help_bold_tt      text_bold_tt        html_bold_tt
69 # help_bullet_tt    text_bullet_tt      html_bullet_tt
70 # help_emit_line    text_emit_line      html_emit_line
71 # help_final        text_final          html_final
72 # help_finishing    text_finishing      html_finishing
73 # help_hot_tt       text_hot_tt         html_hot_tt
74 # help_indent       text_indent         html_indent
75 # help_init         text_init           html_init,  html_init_title
76 # help_italic_tt    text_italic_tt      html_italic_tt
77 # help_line_break   text_line_break     html_line_break
78 # help_list_tt      text_list_tt        html_list_tt
79 # help_line_tt      text_line_tt        html_line_tt
80 # help_quote_tt     text_quote_tt       html_quote_tt
81 # help_section_tt   text_section_tt     html_section_tt
82 # help_target_tt    text_target_tt      html_target_tt
83 # help_title        text_title          html_title
84 # help_underline_tt text_underline_tt   html_underline_tt
86 # help_fixed_styles                     html_enter_list,  html_leave_list
87 # help_proportional_styles              html_enter_pre,   html_leave_pre
88 # fix_target_tt                         html_enter_quote, html_leave_quote
89 #                                       html_emit_header, html_emit_footer
90 #                                       getHtmlAttributes
92 #-----------------------------------------------------------------------------
94 use Getopt::Long;   # for parsing the program command line (GetOptions)
95 use File::Basename; # for trimming off directory names from files (basename)
96 use English;
98 #-------------------------------------------------------------------------------
100 sub emit_version
102     my $version = "1.9";
103     my $date    = "Oct 01, 2003"; 
104     
105     print "$pgm: Version $version, $date.\n";
106     exit 0;
109 #-------------------------------------------------------------------------------
111 sub show_usage
113     print "\n";
114     print "Usage: $pgm [ -dhtTVw ][-D directory][-H [hfile]][-S [htmlExt]] \\\n";
115     print "              [-c conditional][-v name=value][setext_file [converted_file]]\n";
116     print "\n";
117     print "       $pgm {-mp} [-c conditional][-M menuSuffix][-v name=value] setext_file\n";
118     print "\n";
119     print "  The first form of $pgm is used to convert Structure Enhanced TEXT\n";
120     print "  documents into HTML or simple text documents.\n";
121     print "  The second form is specific to generating NEdit help menu code\n";
122     print "  from a setext document with Menu and Help directives.\n";
123     print "\n";
124     print "  -c conditional text definitions, separated by commas.\n";
125     print "  -d do not automatically make titles hypertext references (HTML only)\n";
126     print "  -D specify destination directory for separate HTML files. This also sets\n";
127     print "     the value for the variable HTML_DIR.\n";
128     print "  -h show this usage clause.\n";
129     print "  -H convert setext_file to HyperText Markup Language (HTML).\n";
130     print "     Optional file parameter specifies file containing HTML header\n";
131     print "     and footer definition overrides. The current defaults are:\n";
132     print "        \$htmlHeader = <HTML>\n";
133     print "                      <TITLE>\$HTML_TITLE</TITLE>\n";
134     print "                      <HEAD></HEAD>\n";
135     print "                      <BODY>\n";
136     print "        \$htmlFooter = </BODY>\n";
137     print "                      </HTML>\n";
138     print "     where \$HTML_TITLE is replaced with an appropriate title.\n\n";
139     print "  -m generate NEdit help menu code files.\n";
140     print "  -M name NEdit help code files with this suffix.\n";
141     print "  -p do option -m and print out NEdit help elements.\n";
142     print "  -S convert setext_file into separate HTML files.\n";
143     print "     (the default name extension is '$htmlExt', but it can be\n";
144     print "     changed by specifying it as an argument to this option)\n";
145     print "  -t convert setext_file to simple text (default).\n";
146     print "  -T emit setext typotag definitions in use.\n";
147     print "  -v defines variable name and assigns it the given value.\n";
148     print "     (more than one occurrence of -v can be made) The variables\n";
149     print "     are made available for use within the setext document parsing.\n";
150     print "  -V display the version of this setext script.\n";
151     print "  -w do not emit warnings about missing variables.\n";
152     print "\n";
153     print "  When the converted_file argument is missing, STDOUT is used.\n";
154     print "  When the setext_file argument is missing, STDIN is used.\n";
155     print "\n";
156     print "  To get conditional text within a setext document to be displayed,\n";
157     print "  supply a definition tag through the -c option. For example,\n";
158     print "\n";
159     print "          $pgm -c NEDITDOC help.etx nedit.doc\n";
160     print "\n";
161     print "  would generate a plain text document, nedit.doc, from the source\n";
162     print "  help.etx, including/excluding text marked with 'NEDITDOC'\n";
163     print "  conditional text markers, also known as 'maybe' typotags.\n";
164     print "\n";
166     exit 0;
169 #---------------------------------------------------------------------------
170 # This is a GetOptions call back function for gleaning variables from
171 # the command line so that they can be available to the setext parsing
172 # without having to appear in the setext document. The expected form
173 # on the command line is: -v variableName=value. For example, -v version=5.2
174 #---------------------------------------------------------------------------
175 sub declare_variable
177     my $optionName  = shift;
178     my $optionValue = shift;
179     my ( $varName, $varValue ) = split( "=", $optionValue );
180     
181     $varValue or do { 
182        print STDERR "Missing value for variable '$varName'\n"; 
183        $Getopt::Long::error++; 
184        return 
185        };
186     
187     #-----------------------------------------------------
188     # By trimming off leading and trailing spaces allows
189     # data entry like this: "version = 5.2 of Oct. 2001".
190     #-----------------------------------------------------
191     $varName  =~ s/$trim_spaces/$2/o;
192     $varValue =~ s/$trim_spaces/$2/o;
193     
194     $variables{ $varName } = $varValue;
197 #-------------------------------------------------------------------------------
199 sub emit_setext_definition
201     print <<END_OF_DEFINITION_TEXT;
203 Typotags Available
204 ------------------
206  The following table contains typotags recognized by
207  $pgm. The "setext form" column in the table
208  is formatted such that the left most character of
209  the column represents the first character in a line
210  of setext. The circumflex character (^) means that
211  the characters of the typotag are significant only
212  when they are anchored to the front of the setext
213  line. This definition is a sample of a setext document.
214  Consequently, it must be put through the program so
215  that you can view the actual "setext form" of some
216  of the typotags. Thus, issue the following commands
217  to get a proper text view of the table below.
219      $pgm -T > typotags.etx
220      $pgm -w typotags.etx
222  ============  ===================  ==================
223 !     name of  setext form          acted upon or
224 ! the typotag  of typotag           displayed as
225 !============  ===================  ==================
226 !    title-tt "Title                a title
227 !              ====="               in chosen style
228 !------------  -------------------  ------------------
229 !  subhead-tt "Subhead              a subhead
230 !              -------"             in chosen style
231 !------------  -------------------  ------------------
232 !  section-tt ^#> section-text      a section heading
233 !                                   with '#' from 1..9
234 !                                   in chosen style
235 !------------  -------------------  ------------------
236 !   indent-tt ^  lines indented     lines undented
237 !             ^  by 2 spaces        and unfolded
238 !------------  -------------------  ------------------
239 !     bold-tt      **[multi]word**  1+ bold word(s)
240 !   italic-tt         ~multi word~  1+ italic word(s)
241 !underline-tt       [_multi]_word_  underlined text
242 !      hot-tt        [multi_]word_  1+ hot word(s)
243 !    quote-tt ^>[space][text]       > [mono-spaced]
244 !   bullet-tt ^*[space][text]       [bullet] [text]
245 !  untouch-tt  `_quoted typotag!_`  `_left alone!_`
246 !  notouch-tt ^!followed by text    text-left-alone
247 !    field-tt     |>name[=value]<|  value of name
248 !     line-tt ^   ---               horizontal rule
249 !------------  -------------------  ------------------
250 !     list-tt  .([space]list start  multiple line list
251 !                                   element ends with
252 !                                   empty line
253 !  endlist-tt  .)                   denotes list end
254 !------------  -------------------  ------------------
255 !     href-tt ^.. \@_word URL         jump to address
256 !     note-tt ^.. \@_word Note:("*")  ("cause error")
257 !   target-tt     \@_[multi_]word     [multi ]word
258 !------------  -------------------  ------------------
259 !  twobuck-tt  \$\$ [last on a line]  [parse another]
260 ! suppress-tt ^..[space][not dot]   [line hidden]
261 !   twodot-tt ^..[alone on a line]  [taken note of]
262 !------------  -------------------  ------------------
263 !    maybe-tt ^.. ? name[~] text    show text when
264 !                                   name defined
265 ! maybenot-tt ^.. ! name[~] text    show text when
266 !                                   name NOT defined
267 ! endmaybe-tt ^.. ~ name            end of a multi-
268 !                                   line maybe[not]-tt 
269 !------------  -------------------  ------------------
270 ! passthru-tt ^!![text]             text emitted
271 !                                   without processing
272 !------------  -------------------  ------------------
273 !   escape-tt  @\@x where 'x'  is     x is what remains
274 !              escaped character    @@@@ needed for 1 @@
275  ============  ===================  ==================
277  Only one instance of the element subhead-tt (or, in its
278  absence, title-tt) is absolutely _required_ for a text to
279  be considered a valid setext.
281  All the elements, but subhead-tt, are in effect optional,
282  that is, not necessary for a setext to be declared as
283  such. The target-tt element allows the hypertext link
284  definition of href-tt to be within the same setext. The
285  actual reference (href-tt) of the target would look like:
287     .. _word #reference_within_document
289 !Multiple line maybe[not]-tt (conditional text regions)
290 !are introduced as ".. ? name~" or ".. ! name~" and are
291 !terminated with ".. ~ name", on a separate line. Single
292 !line maybe[not]-tt do not use the '~' character and are
293 !terminated with the end of the line. The special
294 !conditional text region named "html" allows a mixture of
295 !setext and HTML tags. Nesting of these typotags is
296 !allowed. For instance, if there are three conditional
297 !regions, A, B, and C, C can be nested inside B, which can
298 !be nested inside A (eg. A-B-C...C-B-A). Note that a
299 !surrounding region cannot end before one of its inner
300 !regions is terminated (eg. of illegal nesting
301 !A-B-C...C-A-B, where A terminated prior to B.
303  Multiple line list-tt are introduced as ".(". Each line
304  belongs to the current list element until an empty line
305  is encountered. Once a list-tt is encountered, line
306  separated paragraphs constitute list elements.  A list-tt
307  is terminated by endlist-tt. The list-tt/endlist-tt
308  typotags are allowed to be nested (unlike the bullet-tt).
309  These typo-tags do not have to start in the first column
310  of a line, but must have leading whitespace if they are
311  indented at all.
313  Field typotags are used to define and reference values.
314  Field definitions can only occur within a suppress-tt.
315  For example:  "..  `|>author=Steven Haehn<|`"
316  Field references (eg. |>author<|) can occur in any 
317  printable text. If there is no known value for the 
318  field, it will remain unchanged and appear as written
319  in the setext.
320 END_OF_DEFINITION_TEXT
322     #---------------------------------------------------------------
323     # Emit any predefined variables so user knows what is available.
324     #---------------------------------------------------------------
325     if( %variables )
326     {
327         print "\n";
328         print " The following are predefined for use in a field-tt\n";
329         print " for any setext document translated by this utility.\n";
330         print "\n";
331         
332         foreach $key ( sort keys %variables )
333         {
334             print "    $key = $variables{$key}\n";
335         }
336     }
337     
338     print "\n \$\$\n";
339     exit 0;
342 #-------------------------------------------------------------------------------
344 $pgm = basename( $PROGRAM_NAME );
346 #==========================
347 # Global shared definitions
348 #==========================
349 $um          = "\375"; # untouchable marker
350 $vm          = "\374"; # variable marker
351 $escMrk      = "\33";  # internal escape marker
352 $trim_spaces = '(\s*)(.*?)(\s*)$';
353 $list_level  = 0;
354 $listIndent  = 2;
355 @bullet_list = qw( * * o + * o + * o + );
356 %variables   = ( date => &date(), Date => &date("D"), year => &date("y") );
357 @cond_text_definitions = ();
358 $make_title_href = 1;
360 #---------------------------------------
361 # Variables needed for HTML conversions.
362 #---------------------------------------
363 $lt      = "\376"; # "<" marker
364 $gt      = "\377"; # ">" marker
365 $amp     = "\373"; # "&" marker
366 $htmlExt = "html"; # default HTML file name extension
368 $htmlHeader =
369      "<HTML>\n<HEAD>\n" .
370      "<TITLE>\$HTML_TITLE</TITLE>\n" .
371      "</HEAD>\n" .
372      "<BODY>\n";
373      
374 $htmlFooter = "</BODY>\n</HTML>\n";
376 #---------------------------------------------------------
377 # Look for following options, complain about unknown ones.
378 #---------------------------------------------------------
379 Getopt::Long::config( "noignorecase" );
381 GetOptions( 
383     'c=s',  # conditional text definitions, separated by commas
384     'd',    # do not make titles hypertext references (HTML only)
385     'D=s',  # specify destination directory for separate HTML files
386     'h',      \&show_usage,
387     'H:s',  # create HTML from setext input
388     'm',    # create NEdit help menu code from setext input
389     'M=s',  # name NEdit help code files with this suffix
390     'p',    # same as 'm' but with debug printout
391     'S:s',  # generate separate HTML files for each subsection
392     't',    # create text from setext input
393     'T',    # emit setext typo-tag document
394     'v=s',    \&declare_variable,
395     'V',    # emit setext script version information
396     'w'     # do not emit warning messages.
398 ) || &show_usage;
400 #-----------------------------------
401 # Glean only those options specified
402 #-----------------------------------
403 $opt_c && (@cond_text_definitions = split( ",", $opt_c ));
404 $opt_d && ($make_title_href=0);
405 $opt_D && do { $variables{HTML_DIR}=$opt_D; $outputDirectory="$opt_D/" };
406 $opt_h && show_usage();
407 defined $opt_H && do { $convert_to = "html"; getHtmlAttributes( $opt_H ) };
408 $opt_m && do { $make_menu = 1; $convert_to = "help" };
409 $opt_M && ($helpSuffix = $opt_M );
410 $opt_p && do { $make_menu = 1; $convert_to = "help"; $print_menu = 1 };
411 defined $opt_S && do { 
412     $convert_to = "html";
413     $htmlExt = $opt_S if $opt_S; # user can specify file extension
414     $separate_html_files=1; 
415     $make_title_href=1 
416     };
417 $opt_t && ($convert_to = "text");
418 $opt_T && emit_setext_definition();
419 $opt_V && (emit_version());
420 $opt_w && ($noWarn = 1);
422 #--------------------------------------------------------------
423 # Setext Parser states.
425 # The names are used to construct "enter_" & "leave_" elements
426 # in the state_change hash table required to be initialized
427 # by language specific initialization routines (see html_init).
428 #--------------------------------------------------------------
429 $FMT     = "fmt";
430 $LIST    = "list";
431 $PRE_FMT = "pre";
432 $QUOTE   = "quote";
434 #----------------------------
435 # Typotag Pattern Definitions
436 #----------------------------
437 $bold_tt       = '\*\*([^\*]+)\*\*([^\*]|$)';
438 $bullet_tt     = '^\* ([^ ])';
439 $empty_line    = '^\s*$';
440 $fld_left      = '\|>';
441 $fld_right     = '<\|';
442 $field_tt      = "(${fld_left}.+?$fld_right)";
443 $field_content = "${fld_left}(.+?)$fld_right";
444 #$field_tt      = "(${fld_left}[^<]+$fld_right)";
445 #$field_content = "${fld_left}([^<]+)$fld_right";
446 $hot_tt        = '\b([\S]*)_\b';
447 $href_tt       = '^\.\.\s+_([\S]*)\s+(.*)\s*';
448 $indent_tt     = '^  ([^ ])';
449 $intHrefMrk    = "#";
450 $internal_href = "^$intHrefMrk(.*)\$";
451 $italic_tt     = '~([^~]*)~';
452 $line_tt       = '^   ---*$';
453 $list_tt       = '^\s*\.([()])';
454 $notouch_tt    = '^!';
455 $passthru_tt   = '^!!';
456 $quote_tt      = '^> ';
457 $section_tt    = '^([1-6])>';
458 $subtitle_tt   = '^---';
459 $suppress_tt   = '^\.\.';
460 $target_tt     = '(?!(^|\s)_[\S]+_(\s|\W|$))(^|\s)_([\S]+)'; # not underline, then target
461 $title_tt      = '^===';
462 $twobuck_tt    = '^\s*\$\$\s*$';
463 $underline_tt  = '\b_([\S]*)_\b';
464 $untouch_tt    = "\\s*(`[^`]+[`'])(?=\\s|\\W|\$)";
465 $variable_def  = '\s*(\w+)\s*([^=]*(=(.*)))?'; # $1 = name, $4 = value
467 $escape_tt     = "@"; # the character escape symbol (need @@ to escape @)
468 $needEscaping  = "$escape_tt(.)";
469 $escapedFound  = "$escMrk(\\d+)$escMrk";
471 if( $make_menu )
473     $setext_file = $ARGV[0];
474     open SETEXT, "<$setext_file" or die "Can't access $setext_file, $OS_ERROR";
475     make_NEdit_menu_code();
476 }    
477 else  # Global elements for parsing setext
479     #-------------------------
480     # Program option defaults.
481     #-------------------------
482     $setext_file    = "-";  # STDIN, allows program to be used as a filter
483     $converted_file = "-";  # STDOUT
484     $convert_to     = "text" if $convert_to eq "";
485     
486     #--------------------------------------
487     # Begin processing file specifications.
488     #--------------------------------------
489     $setext_file = $ARGV[0] if $ARGV[0] ne "";
490     open SETEXT, "<$setext_file" or die "Can't access $setext_file, $OS_ERROR";
492     if( $ARGV[1] ne "" ) 
493     { 
494         $converted_file = $ARGV[1];
495         $convert_to = "html" if $converted_file =~ /\.$htmlExt$/; # in case -H forgotten
496         
497         if( $converted_file eq basename( $converted_file ) )
498         {
499             if( $outputDirectory )
500             {
501                 $converted_file = "$outputDirectory/$converted_file";
502             }
503         }
504     }
506     open CONVERT, ">$converted_file" or die "Can't create $converted_file, $OS_ERROR";
507     translate_setext();
510 #-------------------------------------------------------------------------------
511 #-------------------------------------------------------------------------------
512 #-------------------------------------------------------------------------------
514 sub translate_setext
516     #--------------------------------------
517     # Adding conversion type to conditional
518     # text definitions for convenience.
519     #--------------------------------------
520     push @cond_text_definitions, $convert_to;
522     get_setext( SETEXT, \@cond_text_definitions, \@data );
523     
524     extract_menu_info( \@data )
525        if( $convert_to eq "html" && $separate_html_files );
527     chomp @data;        # remove the newline character from each line.
528     
529     register_tt_translationFunctions( $convert_to );
531     parse_setext( \@data );
534 #-------------------------------------------------------------------------------
536 sub make_NEdit_menu_code
538     #--------------------------------
539     # Supply a default NEdit version.
540     #--------------------------------
541     $neditDefaultMarker   = "NEdit release of ";
542     $variables{ version } = $neditDefaultMarker . date() 
543        if (not exists $variables{ version } or 
544           $variables{ version } eq "default");
545     
546     #--------------------------------------
547     # Adding conversion type to conditional
548     # text definitions for convenience.
549     #--------------------------------------
550     push @cond_text_definitions, $convert_to;
552     get_setext( SETEXT, \@cond_text_definitions, \@data );
553     extract_menu_info( \@data );
554     register_tt_translationFunctions( $convert_to );
557 #-------------------------------------------------------------------------------
559 sub parse_setext
561     my $setextData = shift;
562     
563     local($crnt_state, $fold, $a, $i, $unt, $lineNo);
565     $crnt_state    = $FMT;
566     $lineNo        = -1;
567     @untouchable   = ();
568     $fold          = 0;
570     foreach (@$setextData) 
571     {
572         $lineNo++;  # current location in data array
573         
574         #--------------------------
575         # process title information
576         #--------------------------
577         (/$title_tt/i or /$subtitle_tt/i) && do { 
578             &$do_title(); 
579             $fold = 0; 
580             next; 
581             };
582             
583         /$section_tt/o && do { 
584             &$do_section_tt($1); 
585             $fold = 0; 
586             next;
587             };
588         
589         /$passthru_tt/ && do { 
590             &$do_emit_line();
591             next;
592             };
593         
594         next if ( /$suppress_tt/ or /$twobuck_tt/ );
596         $list_level = 0 if $list_level < 0;  # paranoia protection
597         
598         #--------------------------------------------------
599         # handle line breaks, only one empty line gets out.
600         #--------------------------------------------------
601         if ( /$empty_line/o ) {
602             to_state( $FMT );
603             
604             if( $list_level and not $fold ) {
605                 &$do_list_tt();
606                 $fold = 1;
607             }
608             else {
609                 $fold = &$do_line_break( $fold );
610             }
611             next; 
612         }
613         
614         $fold = 0;  # no more empty lines
616         /$line_tt/ && do { &$do_line_tt(); next; };
617         
618         #-----------------------------------
619         # No change to current state allowed
620         # during list processing.
621         #-----------------------------------
622         if( $list_level == 0 )
623         {
624             #------------------
625             # State transitions
626             #------------------
627             if    ( /$quote_tt/o  ) { &to_state( $QUOTE )   }
628             elsif ( /$bullet_tt/o ) { &to_state( $LIST )    }
629             elsif ( /$indent_tt/o ) { &to_state( $FMT )     }
630             elsif ( /$list_tt/o   ) { &to_state( $FMT )     }
631             else                    { &to_state( $PRE_FMT ) }
632         }
634         if( /$notouch_tt/o ) { s/$notouch_tt/ /o; }
635         else
636         {
637             #--------------------------------------------
638             # Handle the untouchables first.
639             # Mark their locations for later replacement.
640             # (see recover_extractions)
641             #--------------------------------------------
642             for( $i = scalar( @untouchable ); /$untouch_tt/o; $i++ )
643             {
644                 $unt = $1;
645                 $unlen = length( $unt );
646                 $unloc = index( $_, $unt );
647                 $untouchable[ $i ] = $unt; 
648                 $front = substr( $_, 0, $unloc );
649                 $back  = substr( $_, $unloc+$unlen );
650                 $_ = $front . $um . $back; 
651             }
653             &$do_list_tt();
654             &$do_bullet_tt();
655             &$do_quote_tt();
656             &$do_bold_tt();
657             &$do_italic_tt();
658             &$do_underline_tt();
659             &$do_target_tt();
660             &$do_hot_tt();
661             &$do_indent_tt();
662         }
663         &$do_emit_line();
664     }
665     
666     &$do_final();
669 #-------------------------------------------------------------------------------
671 sub register_tt_translationFunctions
673     my $conversion_type = shift;
674     
675     #----------------------------------------------------
676     # Register call-back functions for typotag processing
677     #----------------------------------------------------
678     $do_bold_tt       = "${conversion_type}_bold_tt";
679     $do_bullet_tt     = "${conversion_type}_bullet_tt";
680     $do_emit_line     = "${conversion_type}_emit_line";
681     $do_final         = "${conversion_type}_final";
682     $do_hot_tt        = "${conversion_type}_hot_tt";
683     $do_indent_tt     = "${conversion_type}_indent";
684     $do_initialize    = "${conversion_type}_init";
685     $do_italic_tt     = "${conversion_type}_italic_tt";
686     $do_line_break    = "${conversion_type}_line_break";
687     $do_line_tt       = "${conversion_type}_line_tt";
688     $do_list_tt       = "${conversion_type}_list_tt";
689     $do_quote_tt      = "${conversion_type}_quote_tt";
690     $do_section_tt    = "${conversion_type}_section_tt";
691     $do_target_tt     = "${conversion_type}_target_tt";
692     $do_title         = "${conversion_type}_title";
693     $do_underline_tt  = "${conversion_type}_underline_tt";
695     &$do_initialize;  # do any necessary initialization
698 #-------------------------------------------------------------------------------
700 sub date
702     $format = $_[0];
703     
704     ( $sec,$min,$hour,$mday,$mon,$year,@ignore ) = localtime( time );
705     $month = (January,February,March,April,May,June,July,
706               August,September,October,November,December)[$mon];
707     $year  = $year + 1900;
708     
709     return $year if $format eq "y";
710     return "$month $mday, $year" if $format eq "D";
711     return substr($month,0,3) . " $mday, $year";
714 #-------------------------------------------------------------------------------
716 sub to_state 
718     my $given_state = shift;
719     
720     if ( $crnt_state ne $given_state ) 
721     {
722         if( exists  $state_change{ "leave_$crnt_state"  } )
723         {
724             $doStateChange = $state_change{ "leave_$crnt_state"  };
725             &$doStateChange();
726         }
728         if( exists  $state_change{ "enter_$given_state" } )
729         {
730             $doStateChange = $state_change{ "enter_$given_state"  };
731             &$doStateChange();
732         }
734         $crnt_state = $given_state;
735     }
738 #-------------------------------------------------------------------------------
740 sub count
742     my $whatToCount = shift;
743     my $line        = shift;
744     my $howMany     = 0;
745     
746     $howMany++ while( $line =~ /$whatToCount/g );
747     return $howMany;
750 #-------------------------------------------------------------------------------
752 sub extract_fields
754     local $_ = shift;
755     local $cond_text_region = shift;
756     
757     my ( $field, $variable, $flen, $floc, $front, $back, $v_name, $v_value );
758     my @variable_list = ();
760     #------------------------------------------
761     # Mark all the escaped character sequences.
762     #------------------------------------------
763     while( /$needEscaping/o )
764     {
765         $subChar = ord( $1 );
766         s/$needEscaping/$escMrk$subChar$escMrk/o;
767     }
769     #-----------------------------------------------------
770     # Collect any field typotags found for later expansion.
771     #-----------------------------------------------------
772     while( /$field_tt/o )
773     {
774         $field = $1;
775         $field =~ /$field_content/ && ( $variable = $1 );
776         
777         if( $variable =~ /$variable_def/ ) {
778         
779             $v_name = $1;
780             
781             #----------------------------------------------
782             # When fields 2 and 3 contain identical strings
783             # then a valid field has been encountered.
784             #----------------------------------------------
785             if( $2 eq $3 ) {
786                 $v_value = $4;
787             }
788             else
789             {
790                 #-----------------------------------
791                 # This is NOT a variable definition.
792                 # Have to recover original string.
793                 #-----------------------------------
794                 $v_name  = "_A_${v_name}_Z_"; #internal name
795                 $v_value = $variable;
796             }
797             
798             #----------------------------------------
799             # Is this only a reference to a variable?
800             #----------------------------------------
801             if ( ! defined $v_value ) { 
802                 #------------------------------------------------
803                 # Only put definitions in the list when it is not
804                 # part of a comment. (comments are not emitted)
805                 #------------------------------------------------
806                 push @variable_list, $v_name unless /$suppress_tt/o;
807             }
808             else {
809                 #------------------------------------------------
810                 # setting the variable ( |>varName = value<| )
811                 # (here $v_value is the value assignment portion)
812                 #------------------------------------------------
813                 if( /$suppress_tt/o ) {
814                     $variables{ $v_name } = 
815                        preserve_html( $v_value, $cond_text_region );
816                 } else {
817                     push @variable_list, $variable;
818                 }
819             }
820         }
821         
822         #--------------------------------------
823         # Remove field and replace with marker.
824         #--------------------------------------
825         $flen  = length( $field );
826         $floc  = index( $_, $field );
827         $front = substr( $_, 0, $floc );
828         $back  = substr( $_, $floc+$flen );
829         $_ = $front . $vm . $back; 
830     }
832     #----------------------------
833     # Fill in any variables found
834     #----------------------------
835     foreach $element ( @variable_list ) 
836     {
837         if( exists $variables{ $element } ) {
838             $value = $variables{ $element }
839         } else {
840             $value = "|>$element<|";
841             print STDERR "$pgm: Undefined variable '$element' used in $setext_file.\n" unless $noWarn;
842         }
843         s/$vm/$value/;
844     }
845     
846     $_ = preserve_html( $_, $cond_text_region );
848     return $_;
851 #-------------------------------------------------------------------------------
853 sub preserve_html
855     my $text             = shift;
856     my $cond_text_region = shift;
858     #--------------------------------------------------------
859     # When in a conditional text region that only applies to
860     # HTML translation, change the angle brackets to internal
861     # definitions that will be fixed later. This should allow
862     # for a mixture of setext and HTML language together.
863     #--------------------------------------------------------
864     if ( $cond_text_region eq "html" )
865     {
866         $text =~ s/</${lt}/go;
867         $text =~ s/>/${gt}/go;
868         $text =~ s/\&/${amp}/go;
869     }
870     
871     return $text;
874 #===================================================================
875 # Import setext data from given data stream and pay attention to
876 # conditional text considerations, as described below.
878 #   ^.. ? name   Conditional text when 'name' is defined.
879 #   ^.. ! name   Conditional text when 'name' is NOT defined.
881 #   ^.. ? name~
882 #         Multiple line conditional text when 'name' is defined.
883 #         (without suppress-tt, will always appear in translated
884 #         document going through non-conditional setext conversion)
885 #   ^.. ~ name
887 #   ^.. ! name~
888 #         Multiple line conditional text when 'name' is NOT defined.
889 #         (without suppress-tt, will always appear in translated
890 #         document going through non-conditional setext conversion)
891 #   ^.. ~ name
893 # This procedure also extracts and applies variable definitions
894 # to the text to be emitted.
895 #===================================================================
897 sub get_setext
899     my $stream = shift;
900     my $cond_text_definitions = shift;
901     my $data = shift;
903     my $conditional_text_marker = '^\.\. ([\?!~])\s*(\S+)\s?(.*)$';
904     my $lineNbr = 0;
905     my $i = 0;
906     my $element = "";
907     my ($tense,$def_nm,$text,$multi_line,$crnt_def);
908     my @cond_text_stack = ();
910     while( $_ = <$stream> )
911     {
912         $lineNbr++;
913         
914         if( /$conditional_text_marker/o )
915         {
916             $tense      = $1;  # positive, negative, or end-of conditional text
917             $def_nm     = $2;
918             $text       = $3;
919             
920             $multi_line = $def_nm =~ s/~//o;
921             
922             #---------------------------------------------
923             # Reach end of multiple line conditional text?
924             #---------------------------------------------
925             if( $tense eq "~" )
926             {
927                 $crnt_def = substr( pop @cond_text_stack, 1 );
928                 
929                 if( $crnt_def ne $def_nm )
930                 {
931                     print STDERR "Incorrectly nested conditinal text sections near line $lineNbr.\n";
932                     print STDERR "Expected end of '$crnt_def', but saw end of '$def_nm'\n";
933                     exit 1;
934                 }
935             }
936             else
937             {
938                 #-----------------------------------------
939                 # Entering multiple line conditional text?
940                 #-----------------------------------------
941                 if( $multi_line ) {
942                     push @cond_text_stack, "$tense$def_nm";
943                 }
944     
945                 #------------------------------------------
946                 # This will also catch any non-space 
947                 # text found on multiple line conditionals.
948                 #------------------------------------------
949                 if( $text =~ /\S/o )
950                 {
951                     $$data[$i++] = extract_fields( "$text\n", $def_nm ) 
952                        if ($tense eq "?") and is_member( $def_nm, $cond_text_definitions );
953                     $$data[$i++] = extract_fields( "$text\n", $def_nm ) 
954                        if ($tense eq "!") and not is_member( $def_nm, $cond_text_definitions );
955                 }
956             }
957         }
958         elsif( scalar( @cond_text_stack ) == 0 )
959         {
960             $$data[$i++] = extract_fields( $_, "" );
961         }
962         else
963         {
964             #--------------------------------------------------------------------
965             # The top element of the conditional text stack is the current
966             # conditional text area. See if it exists in the definitions list.
967             # When present, we want this line of text, depending on 'tense'.
968             #--------------------------------------------------------------------
969             $element = $cond_text_stack[-1];
970             $tense   = substr( $element, 0, 1 );
971             $def_nm  = substr( $element, 1 );
972                 
973             if( $tense eq "?") 
974             {
975                 if( is_member( $def_nm, $cond_text_definitions ) ) {
976                     s/$suppress_tt //o; 
977                     $$data[$i++] = extract_fields( $_, $def_nm );
978                 }
979             }
980             elsif( ! is_member( $def_nm, $cond_text_definitions ) )
981             {
982                 s/$suppress_tt //o;
983                 $$data[$i++] = extract_fields( $_, $def_nm );
984             }
985         }
986     }
989 #-------------------------------------------------------------------------------
991 sub extract_menu_init
993     %MenuNames    = ();
994     @helpMenu     = ();
995     @menuStack    = \@helpMenu;
996     $crntMenu     = \@helpMenu;
997     @indentStack  = ( 0 );
998     $menuLevel    = 0;
999     $comment_ind  = "^\\.\\.";     # setext comment indicator ("..")
1000     $menu_element = "${comment_ind} Menu: ";
1001     $help_element = "${comment_ind} Help: ";
1002     $drop_marker  = "_(.)_";
1003     $separator    = "-";
1004     $help_code    = 9; # special hide-it code indicating not part of help menu
1005     $name_length  = 0; # determines padding alignment in HelpMenu data emission
1006     $crntIndent   = 0;
1007     $subMenuIndicator = "\377";
1010 #-------------------------------------------------------------------------------
1012 sub extract_menu_info
1014     my $thisData  = shift;
1015     my $dataIndex = 0;
1017     extract_menu_init();
1018     
1019     #----------------------------------------------------------------------
1020     # For each and every menu item found in the original data (*.etx) file.
1021     #----------------------------------------------------------------------
1022     while( $_ = get_menu_item( $thisData, \$dataIndex ) )
1023     {
1024         #----------------------------------------------------------------------
1025         # Here we want to extract the menu title, help name, optional hideIt
1026         # numerical indicator, and optional menu association name.
1027         #
1028         # Expecting:  MenuTitle # HelpName [[,]HideItIndicator] [# Association]
1029         #----------------------------------------------------------------------
1030         if( /^([^#]+)#\s*(\w*)(\s*,\s*)?(\d+)?(\s*#\s*)?(\w+)?/o )
1031         {
1032             $menuTitle = $1;
1033             $helpName  = $2;
1034             $hideItInd = ($4 eq "") ? "0" : $4;
1035             $assocName = ($6 eq "") ? $helpName : $6;
1036             $helpName  =~ s/$trim_spaces/$2/;
1037             $assocName =~ s/$trim_spaces/$2/;
1039             #------------------------------------------------
1040             # Determine to which menu this menu item belongs
1041             # using leading whitespace indentation.
1042             # Extract menu character mneumonic.
1043             #------------------------------------------------
1044             $menuTitle =~ /^(\s*)/ && ($nextMenu = length($1)-$crntIndent);
1045             $menuTitle =~ s/$trim_spaces/$2/o;
1047             $mneumonic = (/$drop_marker/) ? $1 : substr( $menuTitle, 0, 1 );
1049             #--------------------------------------------------
1050             # Identation greater than previous menu element
1051             # indicates that this element is part of a submenu.
1052             #--------------------------------------------------
1053             if( $nextMenu > 0 )
1054             {
1055                 @$crntMenu[ $end ] .= $subMenuIndicator; # mark previous element
1056                 ($menu = $previousTitle) =~ s/ /_/g;
1057                 @$menu = ();
1058                 push @menuStack, \@$menu;
1059                 push @indentStack, $nextMenu;
1060                 $menuLevel++;
1061             }
1062             #--------------------------------------
1063             # Indentation less than previous menu 
1064             # element indicates leaving a submenu.
1065             #--------------------------------------
1066             elsif( $nextMenu < 0 )
1067             {
1068                 $indentLevel = $indentStack[$menuLevel] + $nextMenu;
1069                 do {
1070                     pop @menuStack;
1071                     pop @indentStack;
1072                     $menuLevel--;
1074                 } while( $indentLevel < $indentStack[$menuLevel] );
1075             }
1077             $crntIndent += $nextMenu;
1078             $crntMenu    = $menuStack[ $#menuStack ];
1079             $end = scalar( @$crntMenu );
1080             @$crntMenu[ $end ] = "$mneumonic,$menuTitle,$helpName,$hideItInd";
1081             $thisTitle = $menuTitle;
1082             $thisTitle =~ s/$drop_marker/$1/;
1083             $thisTitle =~ s/ /_/go;
1084             $MenuNames{ $thisTitle } = "$menuLevel$assocName";
1085             $previousTitle     = $menuTitle;
1087             #---------------------------------
1088             # update data for padding purposes
1089             #---------------------------------
1090             if( $mneumonic ne $separator and $name_length < length( $helpName ) )
1091             {
1092                 $name_length = length( $helpName );
1093             }
1094         }
1095     }
1098 #-------------------------------------------------------------------------------
1100 sub get_menu_item
1102     my $setext = shift;
1103     my $line   = shift;
1105     #-------------------------------------------
1106     # Search each and every data line for either
1107     # a '# Menu: ' line or a '# Help: ' line and
1108     # return remainder of the line.
1109     #-------------------------------------------
1110     while( $$line < scalar( @$setext ) )
1111     {
1112         $_ = $$setext[ $$line++ ];
1113         return $_               if s/$menu_element//o;
1114         return "$_, $help_code" if s/$help_element//o;
1115     }
1116     
1117     return "";
1120 #-------------------------------------------------------------------------------
1122 sub check_target_reference
1124     local($_) = @_;
1125     my $index;
1126     my $target = "";
1127     
1128     if( /$target_tt/ ){
1129         $target = $4;
1130     }
1131     #-----------------------------------
1132     # Are all titles automatically being
1133     # made into hypertext references?
1134     #-----------------------------------
1135     elsif( $make_title_href ) { 
1136         /$title_tt/ && ( $target = $2 );
1137         $target =~ s/$trim_spaces/$2/o;
1138         $target =~ s/ /_/go;
1139     }
1141     if( $target ) {
1142         print CONVERT "<A NAME=\"$target\"></A>\n";
1143         
1144         # only one target-tt reference of this kind allowed per file.
1145         ($index = is_member( $target, \@nm_ref )) 
1146             && do{ splice( @nm_ref, $index-1, 1 ); };
1147     }
1150 #-------------------------------------------------------------------------------
1152 sub is_member
1154     $item      = shift;
1155     $this_list = shift;
1156     my $index  = 1;
1157     
1158     foreach $member ( @$this_list )
1159     {
1160         if( $item eq $member )
1161         {
1162             return $index;
1163         }
1164         $index++;
1165     }
1166     
1167     return 0;
1170 #-------------------------------------------------------------------------------
1172 sub replace_underlines
1174     my $pattern = shift;
1175     s#$pattern#($text = $1) =~ s,_, ,go; $text; #eg;
1178 #-------------------------------------------------------------------------------
1180 sub reclaim_escapes
1182     #-----------------------------------------
1183     # Put back any escaped characters in text.
1184     #-----------------------------------------
1185     while( /$escapedFound/ )
1186     {
1187         $subChar = chr( $1 );
1188         s/$escapedFound/$escape_tt$subChar/;
1189     }
1192 #-------------------------------------------------------------------------------
1194 sub recover_extractions
1196     my $element;
1198     #------------------------------------
1199     # Replace escaped characters in text.
1200     #------------------------------------
1201     while( /$escapedFound/ )
1202     {
1203         $subChar = chr( $1 );
1204         s/$escapedFound/$subChar/;
1205     }
1207     foreach $element ( @untouchable )   { s/$um/$element/; }
1208     @untouchable = ();
1211 #-------------------------------------------------------------------------------
1213 sub emit_paragraph
1215     my $paragraph = shift;
1216     my $line = $left_margin;
1217     
1218     @words = split ' ', $paragraph;
1220     #-------------------------------------------------------------
1221     # Flow words onto a line up until the right margin is reached.
1222     #-------------------------------------------------------------
1223     foreach $word ( @words )
1224     {
1225         if( length( $line ) + length( $word ) + 1 <= $right_margin )
1226         {
1227             $line = "$line$word ";
1228         }
1229         else
1230         {
1231             print CONVERT "$line\n";
1232             $line = "$left_margin$word ";
1233         }
1234     }
1235     
1236     #--------------------
1237     # emit any remainder.
1238     #--------------------
1239     print CONVERT "$line\n" if( length( $line ) > length( $left_margin ) );
1242 #-------------------------------------------------------------------------------
1244 # This routine is used to override program defaults for HTML settings.
1245 # Two variables are expected to be defined; $htmlHeader and $htmlFooter
1247 sub getHtmlAttributes
1249     my $attrFile = shift;
1250     
1251     if( $attrFile )
1252     {
1253         unless( $return = do $attrFile )
1254         {
1255             warn "Could not parse $attrFile: $@" if $@;
1256             warn "Could not do $attrFile: $!" unless defined $return;
1257             warn "Could not run $attrFile" unless $return;
1258             exit 1;
1259         }
1260     }
1263 #-------------------------------------------------------------------------------
1264 #                setext to text conversion call-back routines.
1265 #                         ( in alphabetical order )
1266 #-------------------------------------------------------------------------------
1268 sub text_bold_tt      { s/$bold_tt/$1$2/g }
1270 #-------------------------------------------------------------------------------
1272 sub text_bullet_tt
1274     #   don't do anything if this is no bulleted line.
1275     if (/$bullet_tt/)
1276     {
1277         #   remove lead-in from paragraph and put the rest in an array
1278         my $paragraph = $_;
1279         $paragraph =~ s/$bullet_tt/$1/;
1280         @words = split ' ', $paragraph;
1281         $paragraph = "";
1282         
1283         #   start with text mode bullet character
1284         my $line = '    * ';
1285         foreach $word (@words)
1286         {
1287             if (length($line) + length($word) + 1 <= $right_margin)
1288             {
1289                 #   put every word in a line if there's still room
1290                 $line = "$line$word ";
1291             } else
1292             {
1293                 #   append line to paragraph if full and start a new line
1294                 $paragraph = "$paragraph$line\n";
1295                 $line = "      $word ";
1296             }
1297         }
1298         #   get last line
1299         $paragraph = "$paragraph$line";
1301         #   remove trailing white space
1302         $paragraph =~ s/\s$//;
1303         $_ = $paragraph;
1304     }
1307 #-------------------------------------------------------------------------------
1309 sub text_emit_line
1311     if( /$passthru_tt/ )
1312     {
1313         s/$passthru_tt//o;  # remove typotag and pass line out as is.
1314         reclaim_escapes();
1315         print CONVERT "$_\n";
1316     }
1317     else
1318     {
1319         #-----------------------------
1320         # Handling nested lists first.
1321         #-----------------------------
1322         if( $list_level )
1323         {
1324             my $pad = " " x ($list_level * $listIndent);
1325             my $bullet = ($atListStart) ? "$bullet_list[$list_level] " : "  ";
1326             s/^\s*/  $pad$bullet/;
1327             $atListStart = 0;
1328         }
1329         elsif( $crnt_state ne $FMT && $text_unfolded_line ne "" )
1330         {
1331             emit_paragraph( &text_finishing($text_unfolded_line) );
1332             $text_unfolded_line = "";
1333         }
1335         #-------------------------------------------
1336         # This keeps extra newlines from popping out
1337         # when a list has been terminated.
1338         #-------------------------------------------
1339         unless( $terminatedList )
1340         {
1341             print CONVERT &text_finishing( $_ ), "\n" unless $_ eq $indentingMode;
1342         }
1343         else
1344         {
1345             $terminatedList = 0;
1346         }
1347     }
1350 #-------------------------------------------------------------------------------
1352 sub text_final { emit_paragraph( &text_finishing($text_unfolded_line) ) }
1354 #-------------------------------------------------------------------------------
1356 sub text_finishing
1358     local($_) = @_;
1359     recover_extractions();
1360     $_;
1363 #-------------------------------------------------------------------------------
1365 sub text_hot_tt
1367     if ( /$hot_tt/ ) {
1368         #---------------------------------------------------------
1369         # The heuristic to prevent Internet addresses from having
1370         # underlines removed, is to check for an '@' character.
1371         #---------------------------------------------------------
1372         if (($text = $1) !~ /\@/ ) {
1373             $text =~ s/_/ /g;
1374         }
1375         s/$hot_tt/$text/;
1376     }
1379 #-------------------------------------------------------------------------------
1381 sub text_indent
1383     s/$indent_tt/$1/o && do {
1384         $text_unfolded_line = "$text_unfolded_line$_ ";
1385         $_ = $indentingMode;
1386     };
1389 #-------------------------------------------------------------------------------
1391 sub text_init         
1393     %state_change = ();
1394     $text_unfolded_line = ""; # to be used by text_indent & text_emit_line
1395     $left_margin   = "  ";    # for emit_paragraph
1396     $right_margin  = 79;      # for emit_paragraph
1397     $indentingMode = "?#.";   # hopefully unique string not normally found
1398     
1399     #----------------------------------------------------------------
1400     # Take all the titles, capitalize and remove title indicator.
1401     #----------------------------------------------------------------
1402     for ($i = 0; $i <= $#data; $i++) 
1403     {
1404         $_ = $data[$i];                 # $_ is default for searches
1406         (/$title_tt/ or /$subtitle_tt/) && do {
1407             $titleType = ( /$title_tt/ ) ? "=" : "-";
1408             $data[$i--] = "..";       # suppress title indicator ( --- or === )
1409             $data[$i]   =~ s/^\s*//o; # get rid of any leading space.
1410             $this_title = $data[$i];
1412             # Have to fix title if it also happens to be a target-tt.
1413             $this_title =~ /$target_tt/ && do { ($tmp = $4) =~ s,_, ,go; $this_title = $tmp };
1414             $data[$i] = "..$titleType \U$this_title";
1415         };
1416     }
1418     #----------------------------------------------------
1419     # NOTE: changing original subtitle-tt search pattern
1420     #       to match what was done above.
1421     #----------------------------------------------------
1422     $subtitle_tt = "^\\.\\.- (.*)";
1423     $title_tt    = "^\\.\\.= (.*)";
1426 #-------------------------------------------------------------------------------
1428 sub text_italic_tt    { s/$italic_tt/$1/g }
1430 #-------------------------------------------------------------------------------
1432 sub text_line_break
1434     my $fold = shift;
1435     emit_paragraph( &text_finishing($text_unfolded_line) );
1436     $text_unfolded_line = "";
1437     print CONVERT "\n" unless $fold++;
1438     return $fold;
1441 #-------------------------------------------------------------------------------
1443 sub text_line_tt      { }
1445 #-------------------------------------------------------------------------------
1447 sub text_list_tt
1449     if( /$list_tt/o )
1450     {
1451         if ( $1 eq '(' )
1452         {
1453             $list_level++;
1454             s/$list_tt\s*//;
1455             $atListStart = 1;
1456         }
1457         else
1458         {
1459             $list_level--;
1460             s/$list_tt//;
1461             $terminatedList = 1;
1462         }
1463     }
1464     
1465     #-----------------------------------------------------
1466     # An empty line terminates a multiple line list entry.
1467     #-----------------------------------------------------
1468     elsif( /$empty_line/o )
1469     {
1470         print CONVERT "\n";
1471         #text_line_break( 0 );
1472         $atListStart = 1 if $list_level;
1473     }
1476 #-------------------------------------------------------------------------------
1478 sub text_quote_tt     { }
1480 #-------------------------------------------------------------------------------
1482 sub text_section_tt
1484     my $hdr_level = shift;
1486     s/$section_tt//;
1487     print CONVERT "\n  \U$_\n" if $hdr_level <= 3;  # converted to uppercase
1488     print CONVERT "\n  $_\n" if $hdr_level > 3;     # left alone
1492 #-------------------------------------------------------------------------------
1494 sub text_target_tt 
1496     s#$target_tt#($text = $4) =~ s,_, ,go; " $text"; #eg;
1499 #-------------------------------------------------------------------------------
1501 sub text_title
1503     my $size;
1504     my $line = "";
1505     my $lc = substr( $_, 2, 1 );
1506     
1507     #-----------------------------------------------------------------
1508     # Incoming text looks like ..= <title text> or ..- <subtitle text>
1509     #-----------------------------------------------------------------
1510     to_state( $FMT ); 
1511     $_ = substr( $_, 4 );
1512     text_target_tt();
1513     $size = length( $_ );
1514     
1515     #-------------------------------------------------
1516     # Going to wrap titles with lines as long as title
1517     #-------------------------------------------------
1518     for( $size = length( $_ ); $size > 0; $size-- )
1519     {
1520         $line="$line$lc";
1521     }
1522     print CONVERT "$line\n$_\n$line\n";
1525 sub text_underline_tt { replace_underlines( $underline_tt ) }
1527 #-------------------------------------------------------------------------------
1528 #                setext to HTML conversion call-back routines.
1529 #                         ( in alphabetical order )
1530 #-------------------------------------------------------------------------------
1532 sub html_bold_tt
1534     #---------------------------------------
1535     # Turn all "**text**" into "<B>text</B>"
1536     #---------------------------------------
1537     s#$bold_tt#${lt}B${gt}$1${lt}/B${gt}$2#g;
1540 #-------------------------------------------------------------------------------
1542 sub html_bullet_tt
1544     s/$bullet_tt(.*)/    ${lt}li${gt}$1$2${lt}\/li${gt}/;
1547 #-------------------------------------------------------------------------------
1549 sub html_emit_footer
1551     print CONVERT "</P>\n$htmlFooter";
1552     $closePgf = "";
1555 #-------------------------------------------------------------------------------
1557 sub html_emit_header
1559     my $htitle = shift;
1560     my $header = $htmlHeader;
1561     
1562     $header =~ s/\$HTML_TITLE/$htitle/o;
1563     print CONVERT $header;
1566 #-------------------------------------------------------------------------------
1568 sub html_emit_line
1570     print CONVERT "<LI>" if $atListStart == 1 and not /${lt}LI${gt}/o;
1571     $atListStart = 0;
1573     if( /$passthru_tt/ )
1574     {
1575         s/$passthru_tt//o;  # remove typotag and pass line out as is.
1576         reclaim_escapes();
1577         print CONVERT "$_\n"
1578     }
1579     else
1580     {
1581         print CONVERT &html_finishing( $_ ), "\n";
1582     }
1585 #-------------------------------------------------------------------------------
1587 sub html_enter_list  { print CONVERT "<UL>\n"  }    # state change activities
1588 sub html_leave_list  { print CONVERT "</UL>\n" }
1590 sub html_enter_pre   { print CONVERT "<PRE>\n";  $insideNoFormatArea = 1 }
1591 sub html_leave_pre   { print CONVERT "</PRE>\n"; $insideNoFormatArea = 0 }
1593 sub html_enter_quote { print CONVERT "<BLOCKQUOTE><PRE>\n";   $insideNoFormatArea = 1 }
1594 sub html_leave_quote { print CONVERT "</PRE></BLOCKQUOTE>\n"; $insideNoFormatArea = 0 }
1596 #-------------------------------------------------------------------------------
1598 sub html_final
1600     &to_state( $FMT );
1601     html_emit_footer();
1603     #----------------------------------------------------
1604     # Report on all internal name references not used up.
1605     #----------------------------------------------------
1606     if( scalar( @nm_ref ) > 0 )
1607     {
1608         print STDERR "\nMissing reference (target-tt) to the following:\n\n";
1609         for( $i=0; $i < scalar( @nm_ref ); $i++ )
1610         {
1611             print STDERR "  $nm_ref[ $i ]\n";
1612         }
1613     }
1616 #-------------------------------------------------------------------------------
1618 sub html_finishing {
1619     local($_) = @_;
1620     my $unt;
1621     s/\&/\&\#38\;/go; s/\</\&\#60\;/go; s/\>/\&\#62\;/go;
1622     s/$lt/</go; s/$gt/>/go; s/$amp/\&/go;   # convert markers to real symbols
1624     #-----------------------------------------------
1625     # This fixes the case where an untouchable 
1626     # string includes these special html characters.
1627     #-----------------------------------------------
1628     foreach $element ( @untouchable )
1629     {
1630         $element =~ s/\&/\&\#38\;/go;
1631         $element =~ s/\</\&\#60\;/go;
1632         $element =~ s/\>/\&\#62\;/go;
1633     }
1634     recover_extractions();
1635     $_;
1638 #-------------------------------------------------------------------------------
1640 sub html_hot_tt
1642     #----------------------------------------------------
1643     # After finding a hot-tt, substitute all underlines
1644     # with spaces and check to see if the hot-tt had
1645     # a corresponding hypertext reference. Flag it in
1646     # bright, bold red when no hypertext record found.
1647     # Allow user to define the variable HTML_DIR as the
1648     # destination directory for the HTML code. 
1649     # Note, the files may have to actually be placed
1650     # in such directory by hand after they are generated.
1651     #----------------------------------------------------
1652     s#$hot_tt#
1653         $h = $href{$1}; ($text = $1) =~ s,_, ,go;
1654         $h ? qq'${lt}A HREF="$variables{HTML_DIR}$h"${gt}$text${lt}/A${gt}' 
1655            : "${lt}B${gt}${lt}font color=red${gt}--> $text <-- NO HREF!!${lt}/font${gt}${lt}/B${gt}"; #eg;
1658 #-------------------------------------------------------------------------------
1660 sub html_indent { s/$indent_tt/$1/ } # get rid of indent-tt characters
1662 #-------------------------------------------------------------------------------
1664 sub html_init
1666     local $title, $aTitle;
1667     my    $target;
1668     
1669     %state_change = (
1670         
1671         enter_list  => "html_enter_list",
1672         leave_list  => "html_leave_list",
1673         
1674         enter_pre   => "html_enter_pre",
1675         leave_pre   => "html_leave_pre",
1677         enter_quote => "html_enter_quote",
1678         leave_quote => "html_leave_quote",
1679     );
1680     
1681     $veryFirstTime = 1;   # used to force table of content header out
1683     #------------------------------------------
1684     # Make a first pass over the data, looking 
1685     # for hypertext linking information.
1686     #------------------------------------------
1687     for ($i = 0; $i <= $#data; $i++) 
1688     {
1689         $_ = $data[$i];                 # $_ is default for searches
1691         #---------------------------------------------------------
1692         # This will pick out targets found in the setext not
1693         # hidden by a suppress-tt, that is, the href-tt below.
1694         # With this check, it is unnecessary to have to include
1695         # the href-tt which uses identical text for internal
1696         # document references. External references need href-tt.
1697         # Have to make sure the match does not pick up elements
1698         # inside a notouch-tt ( eg. `_do_not_want_this_as_target`)
1699         #---------------------------------------------------------
1700         if( /$target_tt/ && substr($`,length($`)-1,1) ne "`" && 
1701             (not /$suppress_tt/) )
1702         {
1703             $href{ $4 } = "$intHrefMrk$4";
1704             push @nm_ref, $4;
1705         }
1706         
1707         #-------------------------------------------------
1708         # Locate HREF's and save. When no target is given,
1709         # assume the target is internal, with same name.
1710         #-------------------------------------------------
1711         if( /$href_tt/  )
1712         {
1713             $hrefID = $1;
1714             $target = ($2) ? $2 : "$intHrefMrk$hrefID"; # assume internal href.
1715             $href{$hrefID} = $2; 
1716                 
1717             #------------------------------
1718             # Remember internal HREF's not 
1719             # already seen for target-tt.
1720             #------------------------------
1721             if( $target =~ /$internal_href/ ) {
1722                 if( not is_member( substr( $target, 1), \@nm_ref ) ) {
1723                     push @nm_ref, $1;
1724                 }
1725             }
1726             next; 
1727         } 
1729         #---------------------------------------------------------
1730         # The first title-tt or subhead-tt gets <TITLE>...</TITLE>
1731         #---------------------------------------------------------
1732         /$title_tt/    && do { $htmlTitle = html_init_title("H1", $i); next; };
1733         /$subtitle_tt/ && do { $htmlTitle = html_init_title("H2", $i); next; };
1734     }
1736     html_emit_header( $htmlTitle );
1737     
1738     #----------------------------------------------------
1739     # NOTE: changing original title-tt search pattern
1740     #       to match what was done in html_init_title.
1741     #----------------------------------------------------
1742     $title_tt = "^\\.\\.\\s+(<H.>)(.*)(<\\/H.>)";
1745 #-------------------------------------------------------------------------------
1747 sub html_init_title
1749     local($head, $i) = @_;
1750     my $hyper_ref;
1751     
1752     $data[$i--] = "..";      # suppress title indicator ( --- or === )
1753     $data[$i]   =~ s/^\s*//; # get rid of any leading space in actual title
1754     $this_title = $data[$i];
1755     
1756     # Have to fix title if it also happens to be a target-tt.
1757     $this_title =~ /$target_tt/ && do { ($tmp = $4) =~ s,_, ,go; $this_title = $tmp };
1758     
1759     #---------------------------------------------------
1760     # Are all titles automatically considered target-tt?
1761     #---------------------------------------------------
1762     if( $make_title_href )
1763     {
1764         $hyper_ref = $this_title;
1765         $hyper_ref =~ s/ /_/go;
1766         $externalReference  =
1767            ($separate_html_files) ? substr("$MenuNames{ $hyper_ref }.$htmlExt", 1) : "";
1769         $href{ $hyper_ref } ="$externalReference$intHrefMrk$hyper_ref";
1771         #------------------------------
1772         # Remember internal HREF's not 
1773         # already seen for target-tt.
1774         #------------------------------
1775         if( not is_member( $hyper_ref, \@nm_ref ) ) {
1776             push @nm_ref, $hyper_ref;
1777         }
1778     }
1779     
1780     #-----------------------------------------------------------------
1781     # Put out the HTML title and then suppress it for later processing
1782     #-----------------------------------------------------------------
1783     $aTitle   = "$this_title" unless $title++;
1784     $data[$i] = ".. <$head> " . $data[$i] . " </$head>";
1785     
1786     return $aTitle;
1789 #-------------------------------------------------------------------------------
1791 sub html_italic_tt
1793     #---------------------------------------
1794     # Turn all "~text~" into "<I>text</I>"
1795     #---------------------------------------
1796     s#$italic_tt#${lt}I${gt}$1${lt}/I${gt}#g;
1799 #-------------------------------------------------------------------------------
1801 sub html_line_break
1803     my $fold = shift;
1804     
1805     print CONVERT "$closePgf<P>\n" unless $fold++;
1806     $closePgf = "</P>";
1807     return $fold;
1810 #-------------------------------------------------------------------------------
1812 sub html_line_tt
1814     if( not $insideNoFormatArea )
1815     {
1816         s/$line_tt/${lt}P${gt}${lt}HR${gt}/;
1817         print CONVERT html_finishing( $_ ), "\n";
1818     }
1821 #-------------------------------------------------------------------------------
1823 sub html_list_tt
1825     if( /$list_tt/o )
1826     {
1827         if ( $1 eq '(' ) # open list level
1828         {
1829             $list_level++;
1830             s/$list_tt/${lt}UL${gt}${lt}LI${gt}/;
1831         }
1832         else             # close list level
1833         {
1834             $list_level--;
1835             s,$list_tt,${lt}/LI${gt}${lt}/UL${gt},;
1836             $atListStart = 2 if $list_level;
1837             $terminatedList = 1;
1838         }
1839     }
1840     
1841     #-----------------------------------------------------
1842     # An empty line terminates a multiple line list entry.
1843     #-----------------------------------------------------
1844     elsif( /$empty_line/o )
1845     {
1846         print CONVERT "</LI>\n" unless $terminatedList;
1847         print CONVERT "</P><P>\n";
1848         $atListStart    = 1 if $list_level;
1849         $terminatedList = 0;
1850     }
1853 #-------------------------------------------------------------------------------
1855 sub html_quote_tt
1857     s/$quote_tt\s*//;
1860 #-------------------------------------------------------------------------------
1862 sub html_section_tt
1864     my $hdr_level = shift;
1865     
1866     print CONVERT "<H$_</H$hdr_level>\n";
1869 #-------------------------------------------------------------------------------
1871 sub html_target_tt
1873     check_target_reference( $_ );
1874     /$target_tt/ && do { ($a = $4) =~ s,_, ,go; s/$target_tt/ $a/; };
1877 #-------------------------------------------------------------------------------
1879 sub html_title
1881     my $titleHolder = $_;
1882     
1883     to_state( $FMT ); 
1884     /$target_tt/ && do { ($a = $4) =~ s,_, ,go; s/$target_tt/$a/; };
1885     
1886     if( /$title_tt/i  ) # this is the new title-tt from html_init
1887     {
1888         $frontMrk = $1; $thisTitle = $2; $backMrk = $3;
1889         
1890         if( not $separate_html_files or $veryFirstTime )
1891         {
1892             check_target_reference( $titleHolder ); 
1893             print CONVERT $frontMrk, &html_finishing($thisTitle), $backMrk, "\n";
1894             $veryFirstTime = 0;
1895         }
1896         elsif( $frontMrk eq "<H1>" )
1897         {
1898             $savedTitle = $thisTitle;
1899             $savedTitleHolder = $titleHolder;
1900         }
1901         else
1902         {
1903             #--------------------------
1904             # Create another HTML file?
1905             #--------------------------
1906             $hyper_ref = $thisTitle;
1907             $hyper_ref =~ s/$trim_spaces/$2/o;
1908             $hyper_ref =~ s/ /_/go;
1909             $association = $MenuNames{ $hyper_ref };
1910             
1911             if( $association ne ""  )
1912             {
1913                 $assocLevel  = substr( $association, 0, 1 );
1914                 $association = substr( $association, 1 );
1915                 $newFile     = "$outputDirectory$association.$htmlExt";
1916             
1917                 if( $converted_file ne $newFile )
1918                 {
1919                     #-----------------------------
1920                     # Finish off the current file.
1921                     #-----------------------------
1922                     html_emit_footer();
1923                     close CONVERT;
1925                     #-----------------------------------------------------
1926                     # This realigns title after nested sublevels complete.
1927                     #-----------------------------------------------------
1928                     if ( $assocLevel == 0 ) {
1929                         $savedTitle = $thisTitle;
1930                     }
1931                     
1932                     $converted_file = $newFile;
1933                     open CONVERT, ">$converted_file" or die "Can't create $converted_file, $OS_ERROR";
1934                     html_emit_header( $savedTitle );
1935                     
1936                     #--------------------------------------------
1937                     # This puts target reference in correct file.
1938                     #--------------------------------------------
1939                     if( $savedTitleHolder )
1940                     {
1941                         check_target_reference( $savedTitleHolder );
1942                         if( $savedTitleHolder =~ /$title_tt/i )
1943                         {
1944                             print CONVERT $1, &html_finishing($2), $3, "\n";
1945                         } 
1946                         $savedTitleHolder = "";
1947                     }
1948                 }
1949             }
1951             check_target_reference( $titleHolder ); 
1952             print CONVERT $frontMrk, &html_finishing($thisTitle), $backMrk, "\n";
1953         }
1954     }
1957 #-------------------------------------------------------------------------------
1959 sub html_underline_tt
1961     #--------------------------------------------
1962     # Turn all "_text_" into "<I><U>text</U></I>"
1963     # Remembering to substitute intervening 
1964     # underlines with spaces.
1965     #--------------------------------------------
1966     s#$underline_tt#
1967        ($text = $1) =~ s,_, ,go; 
1968        "${lt}I${gt}${lt}U${gt}$text${lt}/U${gt}${lt}/I${gt}"; #eg;
1971 #-------------------------------------------------------------------------------
1972 #                setext to NEdit HELP conversion call-back routines.
1973 #                         ( in alphabetical order )
1974 #-------------------------------------------------------------------------------
1976 sub help_bold_tt
1978     #----------------------------------------------------
1979     # Turn all "**text**" into "<stlMrk_B>text<stlMrk_B>"
1980     #----------------------------------------------------
1981     s#$bold_tt#${stlMrk}$TKN_BOLD$1${stlMrk}$TKN_BOLD$2#g;
1984 #-------------------------------------------------------------------------------
1986 sub help_bullet_tt  { s/$bullet_tt/    * $1/ }
1988 #-------------------------------------------------------------------------------
1990 sub help_emit_line  
1992     #------------------------------------------------------
1993     # The following is here to help us generate conditional
1994     # compilation elements for the 'C' compiler.
1995     #------------------------------------------------------
1996     if( /$passthru_tt/ )
1997     {
1998         s/$passthru_tt//o;  # remove typotag and pass line out as is.
1999         reclaim_escapes();
2000         print HELP "$_\n"
2001     }
2002     else
2003     {
2004         #-------------------------------------------------
2005         # This seems to be the only good place to take
2006         # care of style changes that have occurred between
2007         # usage of proportional and fixed font styles.
2008         #-------------------------------------------------
2009         if( $styleChanged )
2010         {
2011             $_ = $styleMark . get_style_name( $crntStyle ) . $_;
2012             $styleChanged = "";
2013         }
2015         my $finishedLine = help_finishing( $_ );
2017         print HELP "\"", $finishedLine, "\",\n";
2019         #----------------------------------------------------------------
2020         # To minimize newline output for the empty line elements,
2021         # the algorithm remembers if its last line had a newline emitted.
2022         #----------------------------------------------------------------
2023         $newLinePresentInLastLine = $finishedLine =~ /\\n$/;
2024     }
2027 #-------------------------------------------------------------------------------
2029 sub help_final  {}
2031 #-------------------------------------------------------------------------------
2033 sub help_finishing  
2035     local($_) = @_;
2037     #----------------------------
2038     # When finishing a heading...
2039     #----------------------------
2040     if( $headingLevel )
2041     {
2042         #--------------------------------------------------
2043         # ... destroy any styles inadvertantly placed there
2044         #--------------------------------------------------
2045         if( /$stlMrk/o )
2046         {
2047             my @line = split $stlMrk;
2048             $_ = join '', @line;
2049         }
2051         #------------------------------------
2052         # ... because only one style allowed.
2053         #------------------------------------
2054         $stlFront     = $styleMark . get_style_name( "" );
2055         $headingLevel = 0;
2056         $stlEnd       = $styleMark . get_style_name( $initialStyle );
2057         $stlFront     = "" if /^$styleToken/ ; # remove redundancy when present
2058         $_            = $stlFront . $_ .$stlEnd;
2059     }
2060     
2061     #---------------------------------------------
2062     # Any style markers found in the current line?
2063     #---------------------------------------------
2064     elsif( /$stlMrk/ )
2065     {
2066         #----------------------------------------
2067         # Break line up into style word elements.
2068         #----------------------------------------
2069         my $line = "";
2070         my @line = split $stlMrk;
2072         foreach $element ( @line )
2073         {
2074             #--------------------------------------------------
2075             # Extract word emphasis token and associated words.
2076             # Embed style marker into text line.
2077             #--------------------------------------------------
2078             $element =~ /^($aStyleToken)?(.*)$/o && do { 
2080                 $token = ($1) ? $1 : $TKN_TEXT;  # $TKN_xxx
2081                 $words = $2;
2082                 my $nextStyle = get_style( $crntStyle, $token );
2084                 if( $crntStyle eq $nextStyle )
2085                 {
2086                     $line .= $words;
2087                 }
2088                 else
2089                 {
2090                     $stlNm = get_style_name( $nextStyle );
2091                     $line .= "$styleMark$stlNm$words";
2092                     $crntStyle = $nextStyle;
2093                 }
2094             };
2095         }
2096         
2097         $_ = $line;
2098     }
2099     
2100     recover_extractions();
2101     fix_target_tt();
2102     
2103     #-------------------------------------------
2104     # Apply any initial style change introduced.
2105     #-------------------------------------------
2106     $_ = $newLeadStyle . $_;
2107     $newLeadStyle = "";
2109     #----------------------------------------------------------------
2110     # Add newline element to all lines which are not being currently
2111     # formatted into a flowing paragraph. It is done here because the
2112     # character also has to get included in the character counts.
2113     #----------------------------------------------------------------
2114     $_ .= get_newline( 1 ) if $crnt_state ne $FMT;
2115     
2116     #----------------------------------------------------------------------
2117     # Since 2 characters (\ and n) are occupying the space of one newline,
2118     # we need to subract out the number of new lines from the total offset.
2119     #----------------------------------------------------------------------
2120     my $styleCount     = count( $styleToken, $_ );
2121     my $newLineCount   = count( "\\\\n", $_ );
2122     my $quoteCount     = count( '"', $_ );
2123     my $backslashCount = count( "\\\\", $_ ) - $styleCount - 
2124                          $newLineCount - $quoteCount;
2125                          
2126     my $adjustment     = ($styleCount * $styleTokenSize) + 
2127                          ($backslashCount / 2) + $newLineCount + $quoteCount;
2128                          
2129     #-----------------------------------------------------------
2130     # Now keep a running total of how many characters to emit.
2131     # (Keep 2 forms, total number for compiler string length
2132     # considerations, and another for target-tt section offsets.
2133     #-----------------------------------------------------------
2134     $sectionCharacterCnt += length( $_ );
2135     $targetOffset        += length( $_ ) - $adjustment;
2136     
2137     $_;
2140 #-------------------------------------------------------------------------------
2142 sub help_fixed_styles
2144     #----------------------------------------------------------------
2145     # All proportional styles in the style state transition table
2146     # begin with the "_" character. If we are already in the
2147     # proportional styles arena, a link, or header, no change occurs.
2148     #----------------------------------------------------------------
2149     if( $crntStyle =~ /^_/ )
2150     {
2151         $crntStyle =~ s/^_//o;
2152         $styleChanged = $crntStyle unless $styleChanged;
2153     }
2156 #-------------------------------------------------------------------------------
2158 sub help_hot_tt
2160     my ( $text, $stlNm, $h );
2161     
2162     #--------------------------------------------------
2163     # After finding a hot-tt, substitute all underlines
2164     # with spaces and check to see if the hot-tt had
2165     # a corresponding hypertext reference. Make it 
2166     # unadorned text when no reference found.
2167     #--------------------------------------------------
2168     s#$hot_tt#
2169         ($text = $1) =~ s,_, ,go; 
2170         $h     = is_known_link( $text );
2171         $stlNm = get_style_name( $crntStyle );
2173         $h ? "$stlMrk$TKN_LINK$text$stlMrk$TKN_LINK"
2174            : $text; 
2175     #eg;
2178 #-------------------------------------------------------------------------------
2180 sub help_indent  
2182     if( /$indent_tt/ )
2183     {
2184         s/$indent_tt/$1/;            # get rid of indent-tt characters
2185         /\S$/ && do { $_ .= ' ' };   # make sure space available for remaining
2186     }                                #  text in this kind of paragraph
2189 #-------------------------------------------------------------------------------
2191 sub help_init
2193     %state_change = (
2194         
2195         enter_pre   => "help_fixed_styles",
2196         leave_pre   => "help_proportional_styles",
2198         enter_quote => "help_fixed_styles",
2199         leave_quote => "help_proportional_styles",
2200     );
2202     #--------------------------------------------
2203     # Global elements needed for making menu code
2204     #--------------------------------------------
2205     %href = ();
2207     $copy_right_holder = "Mark Edel";
2208     $hlptxt            = "help_data$helpSuffix.h";  # name of file holding help data structures
2209     $hlphdr            = "help_topic$helpSuffix.h"; # name of file holding help definitions
2210     $stlMrk            = "\01";          # this is the character code
2211     $styleMark         = '\01';          # this is the text string
2212     $styleToken        = "\\$styleMark"; # this for splitting strings on styleMark
2213     $styleTokenSize    = length( $styleToken ); # accounts for '\01A'
2214     $illegal_help      = "HELP_none";
2216     $menu_record       = "(.),(.*),(.*),(\\d)";
2217     $tgtIndx           = 0;  # target-tt index for hypertext reference array (@href)
2219     #-------------------------------------------------------------------
2220     # The following data is used to embed style data into the help text.
2221     #-------------------------------------------------------------------
2223     #                         TOKENS =>        text        bold        italic      underline
2224     %styles_stt = (
2226     # fixed font styles
2227     
2228       plain     => { style => "A", states => [ "plain",     "bold",      "italic",    "u_plain"    ] },
2229       bold      => { style => "B", states => [ "bold",      "plain",     "b_ital",    "u_bold"     ] },
2230       italic    => { style => "C", states => [ "italic",    "b_ital",    "plain",     "u_italic"   ] },
2231       b_ital    => { style => "D", states => [ "b_ital",    "italic",    "bold",      "u_b_ital"   ] },
2233       u_plain   => { style => "E", states => [ "u_plain",   "u_bold",    "u_italic",  "plain"      ] },
2234       u_bold    => { style => "F", states => [ "u_bold",    "u_plain",   "u_b_ital",  "bold"       ] },
2235       u_italic  => { style => "G", states => [ "u_italic",  "u_b_ital",  "u_plain",   "italic"     ] },
2236       u_b_ital  => { style => "H", states => [ "u_b_ital",  "u_italic",  "u_bold",    "bold_ital"  ] },
2238     #  proportional font styles
2239     
2240       _plain    => { style => "I", states => [ "_plain",    "_bold",     "_italic",   "_u_plain"   ] },
2241       _bold     => { style => "J", states => [ "_bold",     "_plain",    "_b_ital",   "_u_bold"    ] },
2242       _italic   => { style => "K", states => [ "_italic",   "_b_ital",   "_plain",    "_u_italic"  ] },
2243       _b_ital   => { style => "L", states => [ "_b_ital",   "_italic",   "_bold",     "_u_b_ital"  ] },
2245       _u_plain  => { style => "M", states => [ "_u_plain",  "_u_bold",   "_u_italic", "_plain"     ] },
2246       _u_bold   => { style => "N", states => [ "_u_bold",   "_u_plain",  "_u_b_ital", "_bold"      ] },
2247       _u_italic => { style => "O", states => [ "_u_italic", "_u_b_ital", "_u_plain",  "_italic"    ] },
2248       _u_b_ital => { style => "P", states => [ "_u_b_ital", "_u_italic", "_u_bold",   "_bold_ital" ] },
2250     #  hyperLink     style => "Q",
2251     
2252     #  header1       style => "R", --
2253     #  header2       style => "S",   |_ MAX_HEADER
2254     #  header3       style => "T", --
2255     );
2257     #-----------------------------------------------------------
2258     # The link index is the position in a font style table
2259     # where the linking font will reside. It appears immediately
2260     # after the styles from the table above.
2261     #-----------------------------------------------------------
2262     $linkIndex   = scalar( keys %styles_stt );
2263     $maxTokens   = scalar( @{ $styles_stt{plain}{states} } );
2265     $STYLE_PLAIN = $styles_stt{plain}{style};
2266     $STYLE_LINK  = "Q"; # link style marker, a continuation from style table
2267     $STYLE_HDR   = "R"; # beginning of header style markers
2268     $MAX_HEADER  = 3;   # the maximum number of header styles in use
2270     $TKN_TEXT    = 0;   # used in style state transition, order important
2271     $TKN_BOLD    = 1;   # used in style state transition, order important
2272     $TKN_ITALIC  = 2;   # used in style state transition, order important
2273     $TKN_ULINE   = 3;   # used in style state transition, order important
2274     $TKN_LINK    = 4;
2275     
2276     $aStyleToken = "[$TKN_TEXT$TKN_BOLD$TKN_ITALIC$TKN_ULINE$TKN_LINK]";
2278     $initialStyle = "_plain";  # the initial style for help text.
2279     $crntStyle    = $initialStyle;
2280     $headingLevel = 0;
2281     
2282     print_menu( $crntMenu, "" ) if $print_menu;  # sort of debug info
2284     #----------------------------------
2285     # Create help header (help_topic.h)
2286     #----------------------------------
2287     open HLPHDR, ">$hlphdr"  or die "Can't create $hlphdr,  $OS_ERROR";
2288     emit_help_header( HLPHDR, $crntMenu );
2289     close HLPHDR;
2290     
2291     #-------------------------------------------
2292     # Create help text data header (help_data.h)
2293     #-------------------------------------------
2294     open HELP,   ">$hlptxt"  or die "Can't create $hlptxt,  $OS_ERROR";
2295     emit_helpTitles( HELP, $crntMenu );
2297     collect_internal_hypertext_references( \@data );
2298     $whence = 0;
2299     emit_helpText( HELP, $crntMenu, \@data );
2302 #-------------------------------------------------------------------------------
2304 sub help_italic_tt
2306     s/$italic_tt/${stlMrk}$TKN_ITALIC$1${stlMrk}$TKN_ITALIC/g
2309 #-------------------------------------------------------------------------------
2311 sub help_line_break    
2313     my $fold = shift;
2314     $_ .= get_newline( 2 );
2315     help_emit_line() unless $fold++;
2316     return $fold;
2319 #-------------------------------------------------------------------------------
2321 sub help_line_tt       {}
2322 sub help_list_tt       { text_list_tt() }
2323 sub help_quote_tt      {}
2325 #-------------------------------------------------------------------------------
2327 sub help_proportional_styles
2329     #----------------------------------------------------------------
2330     # All proportional styles in the style state transition table
2331     # begin with the "_" character. If we are already in the
2332     # proportional styles arena, a link, or header, no change occurs.
2333     #----------------------------------------------------------------
2334     unless( $crntStyle =~ /^_/   or 
2335             $crntStyle eq "link" or 
2336             $crntStyle eq "header" ) {
2338         $crntStyle = "_$crntStyle";
2339         $newLeadStyle = $styleMark . get_style_name( $crntStyle );
2340     }
2343 #-------------------------------------------------------------------------------
2345 sub help_section_tt    
2347     $headingLevel = shift;
2348     #----------------------------------------------------------
2349     # Heading levels for sectioning are being required to start
2350     # at level 3 (considered the first level). This keeps the
2351     # X-resources down inside NEdit. So here is the mapping.
2352     #  1> level-1
2353     #  2> level-1
2354     #  3> level-1
2355     #  4> level-2
2356     #  5> level-3
2357     #----------------------------------------------------------
2358     $headingLevel = ($headingLevel > 2 ) ? $headingLevel - 2 : 1;
2359     $headingLevel = $MAX_HEADER if $headingLevel > $MAX_HEADER; #
2360     s/$section_tt//;
2361     &help_emit_line;
2362     $crntStyle = $initialStyle;
2366 #-------------------------------------------------------------------------------
2368 sub help_target_tt { } # cannot process target-tt at this time because
2369                        # calculation of the hypertext offset requires
2370                        # a fully expanded text line (see help_finishing).
2372 sub fix_target_tt
2374     if( /$target_tt/ and exists $href{ $4 } )
2375     {
2376         my ( $text, $tgtOffset, $originalLine );
2377         
2378         #---------------------------------------------------
2379         # Have to compute target's offset into help section.
2380         # Need actual text sans styling information. Assuming
2381         # all other text replacement has already occurred.
2382         #---------------------------------------------------
2383         $originalLine = $_;
2384         
2385         s/$styleToken.//g; # remove all styling markers
2386         
2387         #--------------------------------------------------------
2388         # Inside this special substitution, a computation of the
2389         # target's offset from the beginning of the section is
2390         # being computed and applied to the hyper-reference array
2391         # element which will be emitted after all text sections
2392         # have been processed.
2393         #--------------------------------------------------------
2394         s#$target_tt#
2395             ($text = $4) =~ s,_, ,go;
2396             $tgtOffset  = index( $_, $text ) + $targetOffset -1;
2397             $tgtOffset  = sprintf( "%6d", $tgtOffset );
2398             $href[ $tgtIndx++ ] =~ s /^0/$tgtOffset/o;
2399             " $text"; 
2400         #eg;
2401         
2402         #-------------------------------------------------------
2403         # Now fix hyper-references in actual line to be emitted.
2404         #-------------------------------------------------------
2405         $_ = $originalLine;
2406         
2407         s#$target_tt#
2408             ($text = $4) =~ s,_, ,go;
2409             " $text"; 
2410         #eg;
2411     }
2414 #-------------------------------------------------------------------------------
2416 sub help_title         {&help_emit_line}
2418 #-------------------------------------------------------------------------------
2420 sub help_underline_tt
2422     #--------------------------------------------------
2423     # Turn all "_text_" into "<stlMrk_U>text<stlMrk_U>"
2424     # Remembering to substitute intervening 
2425     # underlines with spaces.
2426     #--------------------------------------------------
2427     s#$underline_tt#
2428        ($text = $1) =~ s,_, ,go; 
2429        "${stlMrk}$TKN_ULINE$text${stlMrk}$TKN_ULINE";
2430     #eg;
2433 #-------------------------------------------------------------------------------
2435 sub get_newline
2437     $howMany = shift;
2438     
2439     $howMany-- if $newLinePresentInLastLine && $howMany > 1;
2440     return '\n' x $howMany;
2443 #-------------------------------------------------------------------------------
2445 sub is_known_link
2447     my $linkName = shift;
2448     
2449     for( $index = 0; $index < scalar( @hot_tt_links ); $index++ )
2450     {
2451         $element = $hot_tt_links[ $index ];
2452         return 1 if( $hot_tt_links[ $index ] eq $linkName );
2453     }
2454     
2455     return 0;
2458 #-------------------------------------------------------------------------------
2460 sub get_style
2462     my $crntStyle = shift;    # plain, bold, italic, etc.
2463     my $token     = shift;    # $TKN_xxx
2464     my $style     = "header"; # assume working on header
2465     
2466     if( $headingLevel == 0 )
2467     {
2468         if( $token == $TKN_LINK )
2469         {
2470             if( $crntStyle eq "link" )
2471             {
2472                 $style = $prevStyle;
2473             }
2474             else
2475             {
2476                 $prevStyle = $crntStyle;
2477                 $style = "link";
2478             }
2479         }
2480         else
2481         {
2482             @transitions = @{ $styles_stt{$crntStyle}{states} };
2483             $style = $transitions[ $token ];
2484         }
2485     }
2486     
2487     return $style;
2490 #-------------------------------------------------------------------------------
2492 sub get_style_name
2494     my $crntStyle = shift;    # plain, bold, italic, etc.
2495     my $styleName;
2496     
2497     if( $headingLevel )
2498     {
2499         $styleName = chr(ord( $STYLE_HDR )+$headingLevel-1);
2500     }
2501     elsif( $crntStyle eq "link" )
2502     {
2503         $styleName = $STYLE_LINK;
2504     }
2505     else
2506     {
2507         $styleName = $styles_stt{$crntStyle}{style};
2508     }
2509     
2510     return  $styleName;
2513 #-------------------------------------------------------------------------------
2515 sub get_menu_item
2517     my $setext = shift;
2518     my $line   = shift;
2520     
2521     while( $$line < scalar( @$setext ) )
2522     {
2523         $_ = $$setext[ $$line++ ];
2524         return $_               if s/$menu_element//o;
2525         return "$_, $help_code" if s/$help_element//o;
2526     }
2527     
2528     return "";
2531 #-------------------------------------------------------------------------------
2533 sub print_menu
2535     my $crnt_menu = shift;
2536     my $indent    = shift;
2537     my ( $menuTitle, $mneumonic, $helpName, $hideit, $type );
2538     
2539     foreach $menuItem ( @$crnt_menu )
2540     {
2541         if ( $menuItem =~ /$menu_record/o ) 
2542            { $mneumonic=$1; $menuTitle=$2; $helpName=$3; $hideit=($4) ? $4 : "" }
2543         
2544         if( $hideit eq $help_code ) {
2545             $hideit = ""; 
2546             $type = "Help"
2547         } 
2548         else { 
2549             $hideit = ", ($hideit)" if $hideit;
2550             $type = "Menu" 
2551         }
2552         
2553         print "$type: $indent$mneumonic, $menuTitle [$helpName]$hideit\n";
2554         
2555         if( $menuItem =~ /$subMenuIndicator/o )
2556         {
2557             ($menu = $menuTitle) =~ s/ /_/og;
2558             print_menu( \@$menu, "$indent  " );
2559         }
2560     }
2563 #-------------------------------------------------------------------------------
2565 sub collect_internal_hypertext_references
2567     my $setext = shift;
2568     my $line   = 0;
2569     my ($source, $destination );
2570     
2571     while( $line < scalar( @$setext ) )
2572     {
2573         $_ = $$setext[ $line++ ];
2574         
2575         if( /$href_tt/o )
2576         {
2577             $source      = $1;
2578             $destination = $2;
2579             if( $destination =~ /$internal_href/ )
2580             {
2581                 $href{ $1 } = $source;
2582             }
2583         }
2584     }
2587 #-------------------------------------------------------------------------------
2589 sub emit_helpText
2591     my $stream    = shift;
2592     my $crnt_menu = shift;
2593     my $setext    = shift;
2594     my $line      = 0;
2595     my $index     = 1;
2596     
2597     $helpNameList = "";
2599     emit_help_menu_text( $setext, $stream, $crnt_menu, \$line );
2601     print $stream "static char **HelpText[] = {\n$helpNameList\n};\n\n";
2603     print $stream "HelpMenu H_M [] =\n{\n";
2604     emit_help_menu( $stream, $crnt_menu, 0, 1 );
2605     print $stream "\n};\n";
2606     
2607     #------------------------------------
2608     # Emit internal hypertext references.
2609     #------------------------------------
2610     print $stream "\nHref H_R [] =\n{\n";
2611     $sep = "";
2612     for( $index = 0; $index < scalar( @href ); $index++ )
2613     {
2614         $element = $href[ $index ];
2615         $nextone = ( $index == $#href ) ? "NULL,    " : "&H_R[%2d],";
2616         printf $stream "$sep    { $nextone $element }", $index+1;
2617         $sep = ",\n"
2618     }
2619     print $stream "\n};\n";
2620     
2621     #-----------------------------
2622     # Emit program version string.
2623     #-----------------------------
2624     $pgmVersion = $variables{ version };
2625     $pgmVersion .= '\n' . date() if $pgmVersion !~ /$neditDefaultMarker/;
2626     print $stream "\nstatic const char * NEditVersion = \"$pgmVersion\\n\";\n";
2629 #-------------------------------------------------------------------------------
2631 sub emit_help_menu_text
2633     my $setext    = shift;
2634     my $stream    = shift;
2635     my $crnt_menu = shift;
2636     my $line      = shift;
2637     
2638     my ( $menuTitle, $mneumonic, $helpName, $prevLine );
2639     
2640     #----------------------------------------
2641     # For every node of the menu tree...
2642     #----------------------------------------
2643     foreach $menuItem ( @$crnt_menu )
2644     {
2645         if ( $menuItem =~ /$menu_record/ ) 
2646            {  $mneumonic=$1; $helpName=$3; ($menuTitle=$2) =~ s/_//; }
2647         
2648         #---------------------------------
2649         # ... recursively expand sub-menus
2650         #---------------------------------
2651         if( $menuItem =~ /$subMenuIndicator/ )
2652         {
2653             ($menu = $menuTitle) =~ s/ /_/g;
2654             emit_help_menu_text( $setext, $stream, \@$menu, $line );
2655         }
2656         
2657         elsif( $mneumonic ne $separator ) # ... and not a menu separator
2658         {
2659             locate_menu_text( $setext, $menuTitle, $line )  
2660               or die "Unable to find \"$menuTitle\" text!";
2662             $remainder = "";
2663             my @section = ();
2664             my $lineNbr = 0;
2665             $s_e_p = ($helpNameList) ? ",\n" : "";
2666             $helpNameList .=  $s_e_p . "    htxt_$helpName";
2667             $sectionCharacterCnt = 0;
2668             $targetOffset        = 0;
2669             
2670             #------------------------
2671             # ... emit help menu text
2672             #------------------------
2673             while( 1 )
2674             {
2675                 ($_,$remainder) = get_menu_text( $setext, $remainder, $line );
2676                 
2677                 last if $_ eq "";
2678                 $lineNbr++;
2679                 next if /$empty_line/ and $lineNbr == 1;
2680                 chomp;
2681                 
2682                 #--------------------------------------------------
2683                 # Save all hypertext targets found in current topic
2684                 #--------------------------------------------------
2685                 if( /$target_tt/ and exists $href{ $4 } )
2686                 {
2687                     $target = $4;
2688                     $href   = $href{$target};
2689                     $href   =~ s/_/ /go;
2690                     $target =~ s/_/ /go;
2691                     $topic  = "HELP_\U$helpName,";
2692                     $nl1    = $name_length+6;  # for HELP_ and comma
2694                     push @href, 
2695                        sprintf("0, %-${nl1}.${nl1}s \"$href\", \"$target\"", $topic );
2697                     push @hot_tt_links, $href;  # collect for later verification.
2698                 }
2699                 
2700                 s/\\/\\\\/go;  # escape backslash any where in text
2701                 s/"/\\"/go;    # escape embedded double quotes
2702                 s/^\s*$//;     # redefine whitespace as empty line
2704                 push @section, $_ ;
2705             }
2706             print $stream "static char * htxt_$helpName [] = {\n";
2707             $styleChanged = $initialStyle; # This forces initial style out
2708             $crntStyle    = $initialStyle;
2709             parse_setext( \@section );
2710             print $stream "NULL\n};\n\n";
2711         }
2712     }
2715 #-------------------------------------------------------------------------------
2717 sub locate_menu_text
2719     my $setext    = shift;
2720     my $menuTitle = shift;
2721     my $line      = shift;
2723     $menuTitle =~ s/_//go;   # removing drop key character markers
2724     $menuTitle =~ s/ /./go;  # spaces could be underlines in titles
2725     $menuTitle =~ s/\(/./go; # parens are special in regex searches...
2726     $menuTitle =~ s/\)/./go; #  ... here they should be ignored
2728     #-----------------------------------------------------
2729     # When the whence value is set to zero, the search
2730     # for the text that belongs with the given menu title
2731     # is started at the beginning of the file. This allows
2732     # the menu text to be in an order other than that
2733     # specified by the menu itself. This gives freedom
2734     # to the writer; inefficiency to the text processing.
2735     #-----------------------------------------------------
2736     $$line = 0 if ( $whence != 1 );
2738     while( $$line < scalar( @$setext ) )
2739     {
2740         if( $$setext[ $$line++ ] =~ /$menuTitle/ )
2741         {
2742             if ( $$setext[ $$line ] =~ /$subtitle_tt/  or
2743                  $$setext[ $$line ] =~ /$title_tt/ )
2744             {
2745                 $$line++;
2746                 return 1; # the first line after the setext title marker
2747             }
2748         }
2749     }
2750     
2751     return 0;
2754 #-------------------------------------------------------------------------------
2756 sub get_menu_text
2758     my $setext    = shift;
2759     my $crnt_line = shift;
2760     my $line      = shift;
2761     
2762     #-------------------------------------
2763     # Skip any setext comment lines found.
2764     #-------------------------------------
2765     while( $$setext[ $$line ] =~ /$suppress_tt/ ) { $$line ++ };
2767     $crnt_line = $$setext[ $$line++ ] if $crnt_line eq "";
2768     
2769     if( $crnt_line =~ /$twobuck_tt/ )  # end of setext document?
2770     {
2771         return ("", "");
2772     }
2773     else
2774     {
2775         #--------------------------------------------
2776         # Have to read ahead by one line to catch the
2777         # title of the next section, or the end of
2778         # the setext document.(Eat horizontal rulers)
2779         #--------------------------------------------
2780         do { $_ = $$setext[ $$line++ ] } until not /^   --/;
2782         #--------------------------------
2783         # Look ahead again, so that an
2784         # empty last line is not emitted.
2785         #--------------------------------
2786         if( $crnt_line =~ /^\s*$/ and
2787             ($$setext[ $$line ] =~ /$subtitle_tt/o or 
2788              $$setext[ $$line ] =~ /$title_tt/o or 
2789              $$setext[ $$line ] =~ /$twobuck_tt/o))
2790         {
2791             return ("", "");
2792         }
2793         
2794         if( /$subtitle_tt/o or /$twobuck_tt/o )
2795         {
2796             $$line = $$line - 2;
2797             return ("", "");
2798         }
2799     }
2800     
2801     return ( $crnt_line, $_ );
2804 #-------------------------------------------------------------------------------
2806 sub emit_help_menu
2808     my $stream    = shift;
2809     my $crnt_menu = shift;
2810     my $level     = shift;
2811     my $index     = shift;
2813     my ( $menuTitle, $mneumonic, $helpName, $hideIt );
2814     
2815     if( $level == 0 )
2816     {
2817         $sep = "";
2818         $end_index = scalar( @$crnt_menu );
2819     }
2820     
2821     $level++;
2822     $nl1 = $name_length+6;  # for HELP_ and comma
2823     $nl2 = $name_length+3;  # for 2 double quotes and comma
2824     
2825     #----------------------------------------
2826     # For every node of the menu tree...
2827     #----------------------------------------
2828     foreach $menuItem ( @$crnt_menu )
2829     {
2830         if ( $menuItem =~ /$menu_record/ ) 
2831         {
2832             $mneumonic = $1; 
2833             $helpName  = $3; 
2834             $hideIt    = $4; 
2835             ($menuTitle=$2) =~ s/_//; 
2836         }
2837         
2838         #---------------------------------
2839         # ... recursively expand sub-menus
2840         #---------------------------------
2841         if( $menuItem =~ /$subMenuIndicator/ )
2842         {
2843             ($menu = $menuTitle) =~ s/ /_/g;
2844             printf $stream "$sep    { &H_M[%2d], $level, %-${nl1}.${nl1}s %-${nl2}.${nl2}s $hideIt, '$mneumonic', \"$menuTitle\" }", 
2845                $index, "$illegal_help,", "\"$helpName\",";
2846             $index = emit_help_menu( $stream, \@$menu, $level, $index+1 );
2847         }
2848         
2849         else
2850         {
2851             $topic    = ( $mneumonic eq $separator ) ? "$illegal_help," : "HELP_\U$helpName,";
2852             $helpName = "\"$helpName\",";
2853             $nptr     = ( $end_index == 1 && $level == 1 ) ? "NULL" : "&H_M[%2d]";
2854             
2855             #---------------------------
2856             # are we at end of the menu?
2857             #---------------------------
2858             if( $end_index == 1 && $level == 1 ) {
2859                 print $stream "$sep    { NULL,     ";
2860             }
2861             else {
2862                 printf $stream "$sep    { &H_M[%2d], ", $index;
2863             }
2864             printf $stream "$level, %-${nl1}.${nl1}s %-${nl2}.${nl2}s $hideIt, '$mneumonic', NULL }", $topic, $helpName;
2865             $sep = ",\n";
2866             $index++;
2867         }
2868         
2869         $end_index-- if $level == 1;
2870     }
2871     
2872     return $index;
2875 #-------------------------------------------------------------------------------
2877 sub emit_helpTitles
2879     my $stream    = shift;
2880     my $crnt_menu = shift;
2881     
2882     emit_copyright( $stream, "$hlptxt --  Nirvana Editor help module data" );
2883     print $stream "char *HelpTitles[] = {\n";
2884     emit_help_label( $stream, $crnt_menu );
2885     print $stream "    NULL\n};\n\n";
2888 #-------------------------------------------------------------------------------
2890 sub emit_help_label
2892     my $stream    = shift;
2893     my $crnt_menu = shift;
2894     my ( $menuTitle, $mneumonic, $helpName );
2895     
2896     #-----------------------------------------------------------------
2897     # Emit help title/labels for only the leaf nodes of the menu tree.
2898     #-----------------------------------------------------------------
2899     foreach $menuItem ( @$crnt_menu )
2900     {
2901         if ( $menuItem =~ /$menu_record/ ) 
2902         {
2903             $mneumonic  = $1;
2904             $helpName   = $3; 
2905             ($menuTitle = $2) =~ s/_//go;
2906         }
2907           
2908         if( $menuItem =~ /$subMenuIndicator/ )
2909         {
2910             ($menu = $menuTitle) =~ s/ /_/go;
2911             emit_help_label( $stream, \@$menu );
2912         }
2913         elsif( $mneumonic ne $separator ) # ... and not a menu separator
2914         {
2915             print $stream "    \"$menuTitle\",\n";
2916             push @hot_tt_links, $menuTitle;  # collect for later verification.
2917         }
2918     }
2921 #-------------------------------------------------------------------------------
2923 sub emit_help_header          # populates NEdit's help_topic.h
2925     my $stream    = shift;
2926     my $crnt_menu = shift;
2927     
2928     emit_copyright( $stream, "$hlphdr --  Nirvana Editor help display" );
2929     print $stream "#define MAX_HEADING   $MAX_HEADER\n";
2930     print $stream "#define STL_HD        $linkIndex+1\n";
2931     print $stream "#define STL_LINK      $linkIndex\n";
2932     print $stream "#define STL_NM_HEADER '$STYLE_HDR'\n";
2933     print $stream "#define STL_NM_LINK   '$STYLE_LINK'\n";
2934     print $stream "#define STYLE_MARKER  '$styleMark'\n";
2935     print $stream "#define STYLE_PLAIN   '$STYLE_PLAIN'\n";
2936     print $stream "#define TKN_LIST_SIZE $maxTokens\n";
2937     print $stream "\n";
2938     print $stream "enum HelpTopic {\n";
2939     emit_help_topic( $stream, $crnt_menu );
2940     print $stream "    HELP_LAST_ENTRY,\n";
2941     print $stream "    $illegal_help = 0x7fffffff  /* Illegal topic */ \n";
2942     print $stream "};\n";
2943     print $stream "\n";
2944     print $stream "#define NUM_TOPICS HELP_LAST_ENTRY\n";
2945     print $stream "\n";
2948 #-------------------------------------------------------------------------------
2950 sub emit_help_topic
2952     my $stream    = shift;
2953     my $crnt_menu = shift;
2954     my ( $menuTitle, $mneumonic, $helpName );
2955     
2956     #-----------------------------------------------------------------
2957     # Emit help topic name for only the leaf nodes of the menu tree.
2958     #-----------------------------------------------------------------
2959     foreach $menuItem ( @$crnt_menu )
2960     {
2961         if ( $menuItem =~ /$menu_record/ ) 
2962         {
2963             $mneumonic  = $1;
2964             $helpName   = $3; 
2965             ($menuTitle = $2) =~ s/_//go;
2966         }
2967           
2968         if( $menuItem =~ /$subMenuIndicator/ )
2969         {
2970             ($menu = $menuTitle) =~ s/ /_/go;
2971             emit_help_topic( $stream, \@$menu );
2972         }
2973         elsif( $mneumonic ne $separator ) # ... and not a menu separator
2974         {
2975             print $stream "    HELP_\U$helpName,\n";
2976         }
2977     }
2980 #-------------------------------------------------------------------------------
2982 sub emit_copyright
2984     my $stream   = shift;
2985     my $filename = shift;
2986     
2987     my $year     = date("y");
2988     my $padlen1  = 76 - length( $filename );
2989     my $padlen2  = 52 - length( $copy_right_holder );
2990     my $blanks   = "                                                                                  ";
2991     my $pad1      = substr( $blanks, 0, $padlen1 );
2992     my $pad2      = substr( $blanks, 0, $padlen2 );
2993     
2994     print $stream "/*******************************************************************************\n";
2995     print $stream "*                                                                              *\n";
2996     print $stream "* $filename$pad1 *\n";
2997     print $stream "*                                                                              *\n";
2998     print $stream "                 Generated on " . date() . " (Do NOT edit!)\n";
2999     print $stream "                 Source of content from file $setext_file\n";
3000     print $stream "*                                                                              *\n";
3001     print $stream "* Copyright (c) 1999-$year $copy_right_holder$pad2 *\n";
3002     print $stream "*                                                                              *\n";
3003     print $stream "* This is free software; you can redistribute it and/or modify it under the    *\n";
3004     print $stream "* terms of the GNU General Public License as published by the Free Software    *\n";
3005     print $stream "* Foundation; either version 2 of the License, or (at your option) any later   *\n";
3006     print $stream "* version.                                                                     *\n";
3007     print $stream "*                                                                              *\n";
3008     print $stream "* This software is distributed in the hope that it will be useful, but WITHOUT *\n";
3009     print $stream "* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or        *\n";
3010     print $stream "* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License        *\n";
3011     print $stream "* for more details.                                                            *\n";
3012     print $stream "*                                                                              *\n";
3013     print $stream "* You should have received a copy of the GNU General Public License along with *\n";
3014     print $stream "* software; if not, write to the Free Software Foundation, Inc., 59 Temple     *\n";
3015     print $stream "* Place, Suite 330, Boston, MA  02111-1307 USA                                 *\n";
3016     print $stream "*                                                                              *\n";
3017     print $stream "* Nirvana Text Editor                                                          *\n";
3018     print $stream "* September 10, 1991                                                           *\n";
3019     print $stream "*                                                                              *\n";
3020     print $stream "* Written by $copy_right_holder                                                         *\n";
3021     print $stream "*                                                                              *\n";
3022     print $stream "*******************************************************************************/\n";
3023     print $stream "\n";
3027 #-------------------------------------------------------------------------------
3030 __END__
3032 =head1 NAME
3034 Setext - convert Structured Enhanced TEXT into HTML or plain text.
3036 =head1 SYNOPSIS
3038  Usage: setext [ -dhtTVw ][-D directory][-H [hfile]][-S [htmlExt]] \
3039                [-c conditional][-v name=value][setext_file [converted_file]]
3041         setext {-mp} [-c conditional][-M menuSuffix][-v name=value] setext_file
3043   The first form of setext is used to convert Structure Enhanced TEXT
3044   documents into HTML or simple text documents.
3045   The second form is specific to generating NEdit help menu code
3046   from a setext document with Menu and Help directives.
3048   -c conditional text definitions, separated by commas.
3049   -d do not automatically make titles hypertext references (HTML only)
3050   -D specify destination directory for separate HTML files. This also sets
3051      the value for the variable HTML_DIR.
3052   -h show this usage clause.
3053   -H convert setext_file to HyperText Markup Language (HTML).
3054      Optional file parameter specifies file containing HTML header
3055      and footer definition overrides. The current defaults are:
3056         $htmlHeader = <HTML>
3057                       <TITLE>$HTML_TITLE</TITLE>
3058                       <HEAD></HEAD>
3059                       <BODY>
3060         $htmlFooter = </BODY>
3061                       </HTML>
3062      where $HTML_TITLE is replaced with an appropriate title.
3064   -m generate NEdit help menu code files.
3065   -M name NEdit help code files with this suffix.
3066   -p do option -m and print out NEdit help elements.
3067   -S convert setext_file into separate HTML files.
3068      (the default name extension is 'html', but it can be
3069      changed by specifying it as an argument to this option)
3070   -t convert setext_file to simple text (default).
3071   -T emit setext typotag definitions in use.
3072   -v defines variable name and assigns it the given value.
3073      (more than one occurrence of -v can be made) The variables
3074      are made available for use within the setext document parsing.
3075   -V display the version of this setext script.
3076   -w do not emit warnings about missing variables.
3078   When the converted_file argument is missing, STDOUT is used.
3079   When the setext_file argument is missing, STDIN is used.
3081   To get conditional text within a setext document to be displayed,
3082   supply a definition tag through the -c option. For example,
3084           setext -c NEDITDOC help.etx nedit.doc
3086   would generate a plain text document, nedit.doc, from the source
3087   help.etx, including/excluding text marked with 'NEDITDOC'
3088   conditional text markers, also known as 'maybe' typotags.
3090 =head1 DESCRIPTION
3092 This Structured Enhanced TEXT converter produces either HTML or plain
3093 text files from a given setext source. The HTML files produced can
3094 include hypertext references to within itself, or to external 
3095 destinations. The setext converter also has the capability of providing
3096 different content in the resulting output files through a conditional 
3097 text mechanism, and variable data definitions. All this allows a 
3098 publisher to maintain a single, very readable, source while producing
3099 varying content for different output formats and audiences.
3101 When the converted_file argument is missing, STDOUT is used.
3102 When the setext_file argument is missing, STDIN is used. This gives
3103 setext the capability of being a filter to other programs.
3105 To get conditional text within a setext document to be displayed,
3106 supply a definition tag through the -c option. For example,
3108         setext -c NEDITDOC help.etx nedit.doc
3110 would generate a plain text document, nedit.doc, from the source
3111 help.etx, including/excluding text marked with 'NEDITDOC'
3112 conditional text markers, also known as 'maybe' typotags.
3114 Use the -T option to see the set of typotags supported by this
3115 converter. Further explanations of typotags occurs there.
3117 =head2 HTML Generation Examples
3119 The simplest form of HTML generation is:
3121         setext help.etx nedit.html
3122         setext -H help.etx nedit.html
3124 The results will be stored in the current directory in the nedit.html
3125 file.
3127 When the user wants to break up the resulting html file into multiple
3128 files, with cross references between the files, the -S option should
3129 be used.
3131         setext -S help.etx nedit.html
3133 The resulting files are broken up according to titled sections and 
3134 are placed into the current directory, along with the nedit.html file.
3136 To change the destination of the resulting files, two options are
3137 supplied, the -D and -S options. For instance,
3139         setext -S shtml -D help/nedit help.etx nedit.shtml
3141 The -S option allows the name of the file extension to be altered.
3142 The -D option specifies where the resulting files are going to be
3143 stored. Thus, in the example, all the files will be placed in the
3144 help/nedit directory (relative to the current directory) and will
3145 have ".shtml" as the file extension.
3147 A final nuance has been added to help server side HTML capabilities.
3148 The -H option can be used to specify a file which contains the
3149 definitions of $htmlHeader and $htmlFooter. This will be used to
3150 override that which is supplied by the setext script. For example,
3152         setext -S shtml -H NEdit.ssd help.etx nedit.html
3154 tells setext to use the file NEdit.ssd (server side definition) 
3155 to override the HTML header and footer generation. An example of
3156 the contents of this file follows.
3158     $htmlHeader = 
3159       '<!--#set var="menu" value="documentation" -->' . "\n" .
3160       '<!--#include virtual="/head.shtml"-->' . "\n";
3162     $htmlFooter =  
3163        '<!--#include virtual="/tail.shtml"-->' . "\n";
3165 =head2 NEdit Help Menu
3167 When generating the NEdit help menu code, two files will be produced,
3168 help_data.h and help_topic.h (when the -M option is not used).
3169 These two files contain all the programmatic
3170 data needed to implement hypertext menus within the NEdit program.
3171 The following is an example of a setext invocation which assumes that
3172 the variable 'version' is being used within the help.etx file.
3174         setext -m -v "version=6.0" help.etx
3176 If the -M option is used, its value is appended to the root portion
3177 of the two generated files. For example,
3179         setext -m -c VMS -M _VMS help.etx
3181 will generate the files help_topic_VMS.h and help_data_VMS.h. The 
3182 conditional portion of the help menu specifically designated for VMS 
3183 will be extracted from the help.etx source.
3185 Below is what is used to guide the generation of 'C'-Motif menus.
3186 Indentation is SIGNIFICANT in the "Menu" directive lines below. It
3187 is used to determine under which menu element another item will belong.
3188 The number of spaces indented is not significant, but items to be placed
3189 in the same menu panel MUST line up at the same indent level.
3190 ALL nodes of this menu "tree" should have help name qualifiers.
3191 These are used to produce the internal lists used by NEdit help code.
3193 By default, the first character of the menu element will be used as a 
3194 menu mneumonic key. To use another character in the menu element for 
3195 this purpose, surround the character with underscores (eg. I w_a_nt 'a').
3197 The menu title MUST match the one found in the actual help text (sans
3198 special mneumonic key character marking). The help text title may include
3199 underlines (for spaces) when it is a hyperlink target.
3201 The Help-name is used to generate various data structure names. For
3202 instance, the 'start' help name will be used to generate the HelpTopic
3203 enumeration value HELP_START and the character array htxt_start which
3204 holds the actual help text used in the menu dialogs. Consequently, these
3205 names need to be unique and contain only the characters that a 'C'
3206 compiler can digest.
3208 Menu separator lines use a dash (-) character for the Menu Title. They
3209 should also have a unique Help-name.
3211 A numerical value following the Help-name (separated from the name by
3212 a comma and/or spaces) is part of a menu element hiding scheme implemented
3213 in buildHelpMenu (found in 'menu.c'). When the number matches the hideIt
3214 value found in the procedure, that element will effectively become invisible.
3215 This mechanism was created for particular menu features that are not
3216 available to all incarnations of NEdit (in this case, the VMS version).
3218 A "Help" directive is used for all other text used as NEdit help, but
3219 does not show up in the Help menu. The following is a sample of
3220 Menu and Help directives.
3222 ..       Menu Title                         # Help-name
3223 .. ------------------------------------------------------------
3224 .. Menu: Getting Started                    # start
3225 .. Menu: Basic Operation                    # basicOp
3226 .. Menu:   Selecting Text                   # select
3227 .. Menu:   Finding and Replacing Text       # search
3228 .. Menu:   Cut and Paste                    # clipboard
3229 .. Menu:   Using the Mouse                  # mouse
3230 .. Menu:   Keyboard Shortcuts               # keyboard
3231 .. Menu:   S_h_ifting and Filling           # fill
3232 .. Menu:   F_i_le Format                    # format
3234 .. Menu: Features for Programming           # features
3235 .. Menu:   Programming with NEdit           # programmer
3236 .. Menu:   Tabs/Emulated Tabs               # tabs
3237 .. Menu:   Auto/Smart Indent                # indent
3238 .. Menu:   Syntax Highlighting              # syntax
3239 .. Menu:   Finding Declarations (ctags)     # tags
3241 .. Menu: Regular Expressions                # regex
3242 .. Menu:   Basic Syntax                     # basicSyntax
3243 .. Menu:   Metacharacters                   # escapeSequences
3244 .. Menu:   Parenthetical Constructs         # parenConstructs
3245 .. Menu:   Advanced Topics                  # advancedTopics
3246 .. Menu:   Examples                         # examples
3248 .. Menu: Macro/Shell Extensions             # extensions
3249 .. Menu:   Shell Commands and Filters       # shell, 1
3250 .. Menu:   Learn/Replay                     # learn
3251 .. Menu:   Macro Language                   # macro_lang
3252 .. Menu:   M_a_cro Subroutines              # macro_subrs
3253 .. Menu:   Action Routines                  # actions
3255 .. Menu: Customizing                        # customizing
3256 .. Menu:   Customizing NEdit                # customize
3257 .. Menu:   Preferences                      # preferences
3258 .. Menu:   X Resources                      # resources
3259 .. Menu:   Key Binding                      # binding
3260 .. Menu:   Highlighting Patterns            # patterns
3261 .. Menu:   Smart Indent Macros              # smart_indent
3263 .. Menu: NEdit Command Line                 # command_line
3264 .. Menu: Client/Server Mode                 # server
3265 .. Menu: Cr_a_sh Recovery                   # recovery
3266 .. Menu: ---------------------------------- # separator1
3267 .. Menu: Version                            # version
3268 .. Menu: Distribution Policy                # distribution
3269 .. Menu: Mailing _L_ists                    # mailing_list
3270 .. Menu: Problems/Defects                   # defects
3271 .. ------------------------------------------------------------
3272 .. Help: Tabs Dialog                        # tabs_dialog
3274 =cut