home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / qtawkos2.zip / QTAUTL.ZIP / fmtdoc.exp < prev    next >
Text File  |  1994-11-02  |  34KB  |  1,106 lines

  1. # QTAwk program to format ASCII text files
  2. # (C) Copyright 1990 - 1994 Terry D. Boldt, All Rights Reserved
  3. #
  4. # input: ASCII text file
  5. # output: text formatted into lines of <= 'width' characters
  6. #
  7. # advantages:
  8. # 1) can change easily to suit needs - can add/delete control words as needed
  9. # 2) simple and will easily handle the majority of needs for letters,
  10. #    documents, etc.
  11. #
  12. # design philosophy:
  13. # 1) bare minimum of control words in text
  14. # 2) capture more complex formatting tasks in functions, e.g.,
  15. #    a) page top margin and running header captured in variables set in BEGIN
  16. #       and print_top function
  17. #    b) page bottom margin and running footer captured in variables set in
  18. #       BEGIN and end_page function
  19. # 3) blank lines cause 'break' in text and line skip
  20. #
  21. # control words:
  22. # .*         ==> comment line - ignore
  23. # .br text   ==> break and accumulate text for output
  24. # .ce text   ==> center 'text' in width
  25. # .ds        ==> double space
  26. # .hn        ==> 0 <= n <= 9 header command
  27. # .il n      ==> indent line n spaces from left indentation
  28. # .im filename ==> imbed file named, only one level of imbedding allowed
  29. # .in [[+/-]n] ==> indentation
  30. # .ll n      ==> line length to n
  31. # .hr [on/off][header line ==> turn header printing on/off, set header line
  32. # .fr [on/off][footer line ==> turn footer printing on/off, set footer line
  33. # .pa [n]    ==> page eject n times, n == 1 default
  34. # .pn [roman/arabic/alpha/normal/n] ==> set page number & style
  35. # .sp [n]    ==> n blank lines, n == 1 default
  36. # .ss        ==> single space
  37. # .sx /left/center/right/ ==> split text left - center - right
  38. #
  39. # .pl set page length --> number of lines (includes top/bottom margins & headers
  40. # .tm [n] ==> set top    margin to n lines
  41. # .bm [n] ==> set bottom margin to n lines
  42. #
  43. # .tb n text ==> tab n spaces after left margin and print text,
  44. #                n == 0 causes break
  45. # .tc [n] ==> reserve n pages for table of contents, n == 1 by default
  46. # .pt line ==> insert "line" in table of contents. Note .tc control must be
  47. #               used or Table of Contents not generated
  48. #
  49. # Table formator definition:
  50. # .td (table definition) define separator for input column text
  51. #                        define start columns for each column of table
  52. # .td " 1 7 14
  53. # " ==> separator for each column in input text
  54. # first  column starts at column  1
  55. # second column starts at column  7
  56. # third  column starts at column 14
  57. # column numbering starts at left margin
  58. #
  59. # .td c cols ==> define table separator character and columns
  60. # .tl start  ==> start table formatting
  61. # .tl stop   ==> stop  table formatting
  62. #
  63. # :ol.       ==> start    ordered list
  64. # :sl.       ==> start simple     list
  65. # :ul.       ==> start un-ordered list
  66. # :li.       ==> list item
  67. # :eol.      ==> stop list (ordered)
  68. # :esl.      ==> stop list (simple)
  69. # :eul.      ==> stop list (unordered)
  70. #
  71. # .se name = value ==> set substution variable to remainder of input record
  72. #         formatted "built-in" variables may be defined in 'BEGIN' and
  73. #         'INITIAL' sections also.
  74. #
  75. # .se on/off turn variable replacement on/off - default is on at start
  76. #
  77. BEGIN {
  78.     lstb[0] = '^';
  79.     lstb[1] = '.';
  80.     lstb[2] = ')';
  81.     lstb[3] = ':';
  82.     lstb[4] = '|';
  83.     lstb[5] = '>';
  84.     lstb[6] = '}';
  85.     lstb[7] = ']';
  86.     lstb[8] = '*';
  87.     lstb[9] = '_';
  88.  
  89.      # bullets for un-ordered lists
  90.     blst_odd  = '■';
  91.     blst_even = '-';
  92.  
  93.     olstn = 10;
  94.  
  95.     d_page_length = page_length = 66;   # page length at 6 lines per inch
  96.     d_top_margin = top_margin = 6;     # number of lines reserved for top margin
  97.     d_bot_margin = bot_margin = 6;     # number of lines reserved for bottom margin
  98.     d_width = 65;       # default width of text == line length
  99.  
  100.     width = d_width;    # width of text == line length
  101.     width_cnt = 0;      # increment for printer control code inserts
  102.     d_left_m = 8;       # default number of blanks on left margin
  103.     left_m = 8;         # number of blanks on left margin
  104.  
  105.     header = FALSE;
  106.     footer = TRUE;
  107.  
  108.     sender_tabs = "35"; # set number of tabs for sender of letter
  109.  
  110.     page_num_sym = /#/; # symbol to use for page number in header/footer
  111.  
  112.     hdr_line = dhdr_line = "||- # -||";   # header line
  113.     ftr_line = dftr_line = "||- # -||";   # footer line
  114.  
  115.     toc_hdr_line = "|||Table of Contents|";   # header line
  116.     toc_ftr_line = dftr_line;   # Footer line
  117.  
  118.     hdr_lines = 1;      # number of lines in page header
  119.     hdr_skip = 2;       # number of blank lines after header line(s)
  120.  
  121.     ftr_lines = 1;      # number of lines in page footer
  122.     ftr_skip = 2;       # number of blank lines before footer line(s)
  123.  
  124.     # set variable to split text into pages
  125.     paginate = TRUE;
  126.  
  127.     # default number of text body lines/page
  128.     # == 54 lines default
  129.     num_lines = page_length - top_margin - bot_margin - (header * (hdr_lines - hdr_skip)) - (footer * (ftr_lines - ftr_skip));
  130.  
  131.     ttl_page_cnt = 1;   # indexpendent count of pages
  132.     page_num = 1;       # page number
  133.  
  134.     page_num_prefix = "";   # page number prefix string
  135.     reset_headr_0 = FALSE;  # flag to set page number to 1 on ".ho" command
  136.     left_margin = copies(" ",left_m); # left margin blanks
  137.     double_space = FALSE;   # double spacing output
  138.     roman_num = FALSE;  # page numbering in arabic or roman
  139.     alpha_num = FALSE;  # page numbering in alphabetic
  140.     adjust = 5; # justify lines if size within adjust of width
  141.     wait_on_page = FALSE;   # TRUE if wait at end of page
  142.     para_indent = 1;    # number of blanks less one - first
  143.                         # line of para. indented
  144.     para_start = FALSE; # flag whether line starts new para.
  145.     format = TRUE;      # format flag
  146.     right_justify = 10; # format value to right justify text
  147.     left_justify  = 20; # format value to left  justify text
  148.  
  149.     # printer command string for new line
  150.     printer_command_before = "";
  151.     printer_command_after  = "";
  152.  
  153.     # printer command issued for one line only
  154.     # this variable holds shut-off command
  155.     ptr_one_line = FALSE;
  156.  
  157.     # variable to set for underscoring
  158.     cus_command = FALSE;
  159.  
  160.     # variable to set for paper output
  161.     # first test whether variable has been set on the command line via the '-v' option
  162.     # the type is '0' if it has not been set. If set on command line, then do NOT reset,
  163.     # otherwise set to TRUE, i.e., assume output will be going to paper.
  164.     if ( e_type(paper_output) == 0 ) paper_output = TRUE;
  165.  
  166.     table_format = format;# variable to hold format value while doing table
  167.  
  168.     var_replace = TRUE; # default to replace variables
  169.  
  170.     req_be = /\x0ff/; # required blank - translated to blank just prior to output
  171.                     # above regular expression version
  172.  
  173.     req_bs = '\x0ff'; # required blank - translated to blank just prior to output
  174.                     # above string version
  175.                     # need both regular expression version and string version
  176.                     # to get speed of r.e. search and replacement
  177.                     # and string to place hexadecimal character in output
  178.  
  179.     sysyear   = sdate("%Y");
  180.     sysmonth  = sdate("%m");
  181.     sysdayofm = sdate("%d");
  182.     sysdayofw = sdate("%w");
  183.     sysdayofy = sdate("%j");
  184.  
  185.     date      = sdate("%A, %B %d, %Y"); # set document format date
  186.  
  187.     time      = stime("%I:%M %p");      # set document format time
  188.  
  189.     fnum = /^{_i}$/;    # regular expression for recognizing numeric fields
  190.     stx_chr = ' ';      # character used by split_text function for mask line
  191.  
  192.     stderr = "stderr";
  193.     stdin = "stdin";
  194.     keyboard = "keyboard";
  195.     cstart = "start";
  196.     cstop = "stop";
  197.     no = "no";
  198.     yes = "yes";
  199.     on = "on";
  200.     off = "off";
  201.     right = "right";
  202.     left = "left";
  203.  
  204.     hnum = major_sctn = minor_sctn = 0;
  205.  
  206.     imbedded_file = FALSE;
  207.  
  208.     tc_cnt = 0;
  209.  
  210. #
  211. # define regular expression used to define word at end of sentence
  212. # look for period at end of word (possible double quote between period
  213. # and end of word)
  214.     end_of_sentence = /\."?$/;
  215.      # following variable sets whether an extra blank is inserted
  216.      # between sentences. If TRUE, insert extra blank, if FALSE, do not.
  217.     double_blank = TRUE;
  218.  
  219. #   remove_trail = /{req_bs}+$/;
  220. #
  221. # following variables define printer control sequences
  222. # currently set for IBM Proprinter
  223. #
  224. #   turn emphasized printing on/on
  225.     emph_on  = "\x01b\x045";
  226.     emph_off = "\x01b\x046";
  227.     emph_req_on  = '\x0fc'; # translated just prior to output
  228.     emph_req_off = '\x0fb'; # translated just prior to output
  229. # Continuous Underscore on/off
  230.     cus_on  = "\x01b-\x001";
  231.     cus_off = "\x01b-\x080";
  232.     cus_req_on  = '\x0ee'; # translated just prior to output
  233.     cus_req_off = '\x0ed'; # translated just prior to output
  234. # Condensed Print on/off
  235.     condsd_on  = '\x00f';
  236.     condsd_off = '\x012';
  237. # start/stop Letter Quality Print - proprinter
  238.     lttrq_on  = "\x01b\I2";
  239.     lttrq_off = "\x01b\I0";
  240. # sub/super-script
  241.     sub_on = "\x01bS\x001";
  242.     sup_on = "\x01bS\x080";
  243. # Cancel sub/super-script Printing
  244.     sub_sup_off = "\x01bT";
  245. # set ribbon color - color printer
  246. #   band_1 = "\x01by";      # yellow
  247. #   band_2 = "\x01bm";      # red
  248. #   band_3 = "\x01bc";      # blue
  249. #   band_4 = "\x01bb";      # black
  250. #
  251. # intialize printing - print header blank lines
  252. # use in BEGIN   actions for single document from many
  253. # use in INITIAL actions for many single documents
  254. #
  255. }      # remove comment at line beginning for many documents
  256.  
  257. INITIAL { # remove comment at line beginning for many documents
  258. #
  259. # printer initialization strings would be printed here, e.g.,
  260. # to turn on built-in letter quality print, the following
  261. # print statement is used:
  262.     if ( paper_output ) printf(lttrq_on);
  263.     top = TRUE;
  264. }
  265.  
  266.  
  267. GROUP /^{_w}*$/ { # blank input line - allow for possible blanks and tabs
  268.     printline(no);
  269.     if ( !top ) output_line("",TRUE);   # do not output blanks if at top of page
  270.     para_start = TRUE;
  271.     size += para_indent;
  272.     next;
  273. }
  274.  
  275. GROUP /^\.\*/ {   # comment line - ignore
  276.     next;
  277. }
  278.  
  279. # replace defined variables in format {named_variable} in input record
  280.     {
  281.     if ( var_replace ) $0 = replace($0); # replace variables
  282. }
  283.  
  284. GROUP /^\.sr/ {   # set from addressee
  285.     sender[sr_line++] = strim(substr($0,4));
  286.     $1 = ".tb " ∩ sender_tabs;
  287.     cycle;
  288. }
  289.  
  290. GROUP /^\.ad/ {   # format addressee
  291.     addressee[ad_line++] = strim(substr($0,4));
  292.     $1 = ".br";
  293.     cycle;
  294. }
  295.  
  296. GROUP /^\.fo/ {   # format on/off command
  297.     switch ( $2 ) {
  298.         case on:
  299.             format = TRUE;
  300.             break;
  301.         case off:
  302.             format = FALSE;
  303.             break;
  304.         case right:
  305.             format = right_justify;
  306.             break;
  307.         case left:
  308.             format = left_justify;
  309.             break;
  310.         default:
  311.             fprintf(stderr,"Incorrect format '.fo' command.\nFile: %s\nLine: %u\n",FILENAME,FNR);
  312.             exit 2;
  313.             break;
  314.     }
  315.     printline(no);
  316.     next;
  317. }
  318.  
  319. GROUP /^\.br/ { # break command
  320.     printline(no);
  321.     if ( NF > 1 ) {
  322.         $0 = strim(substr($0,4));
  323.         add_line;
  324.     }
  325.     next;
  326. }
  327.  
  328. GROUP /^\.ce/ {   # command to center line
  329.     local tcnt, tline;
  330.  
  331.     printline(no);
  332.     tcnt = split(strim(substr($0,4)),tline);
  333.     tline = form_line(tline,tcnt,width,left_justify);
  334.     output_line(center(tline,width),TRUE);
  335.     next;
  336. }
  337.  
  338. GROUP /^\.il/ {   # indent next line n spaces
  339.     printline(no);
  340.     if ( $2 ~~ fnum )                   # check if 2nd field an integer
  341.       addword(copies(" ",$2));
  342.     next;
  343. }
  344.  
  345. GROUP /^\.im/ {
  346.     if ( !imbedded_file ) {
  347.         ARGI --;
  348.         FILENAME = $2;
  349.         imbedded_file = TRUE;
  350.         fprintf(stderr,"Imbedded File: %s [Page: %s (%u)\n",FILENAME,set_pnum(page_num),ttl_page_cnt);
  351.       } else {
  352.         fprintf(stderr,"Only One Level Of File Imbedding Allowed.\nFile: %s\nLine: %u\n",FILENAME,FNR);
  353.         exit 200;
  354.     }
  355.     next;
  356. }
  357.  
  358. GROUP /^\.in/ {   # set indentation
  359.     if ( $2 ~~ fnum ) {
  360.         if ( $2 ~~ /^[+-]/ ) {
  361.             left_m += $2;
  362.             if ( left_m < 0 ) left_m = d_left_m;
  363.         } else left_m = $2;
  364.     } else left_m = d_left_m;
  365.     left_margin = copies(" ",left_m);
  366.     next;
  367. }
  368.  
  369. GROUP /^\.ll/ {   # set line length
  370.     if ( $2 ~~ fnum ) width = $2;       # check if 2nd field an integer
  371.       else width = d_width;                 # set line length if TRUE
  372.     next;
  373. }
  374.  
  375. GROUP /^\.h[0-9]/ {
  376.     local hl, i, hnm;
  377.  
  378.     printline(no);
  379.     hl = int(substr($$0.0,3,1) + 0);
  380. #    print "<>",$$0.0,substr($$0.0,3,1),hl"<>";
  381.     if ( hl < head_level ) {
  382.         for ( i = hl + 1 ; i < 10 ; i++ ) hla[i] = 0;
  383.       } else if ( hl > head_level + 1 ) {
  384.         fprintf(stderr,"Error - Incorrect Head Level Specified: %u.\nCurrent Head Level: %u.\nInput File: %s\nRecord Number: %u",hl,head_level,FILENAME,FNR);
  385.         exit 10;
  386.     }
  387.     if ( !(head_level = hl) ) {
  388.         if ( !top ) page_eject;
  389.         if ( !(page_num & 1) ) page_eject;
  390.         if ( reset_headr_0 ) page_num = 1;
  391.     }
  392.     ++hla[hl];
  393.     hnum = hla[0] "";
  394.     if ( hl ) {
  395.         minor_sctn = hla[hl];
  396.         for ( i = 1 ; i <= hl ; i++ ) hnum ∩= "." ∩ hla[i];
  397.         hnm = hnum;
  398.       } else {
  399.         major_sctn++;
  400.         minor_sctn = 0;
  401.         hnum ∩= ".0";
  402.         hnm = cus_req_on ∩ hnum;
  403.         toc_lines[tc_cnt]["skip"] = TRUE;
  404.     }
  405.     $1 = hnum;
  406.     toc_lines[tc_cnt]["line"] = $0;
  407.     toc_lines[tc_cnt]["pgn"] = set_pnum(page_num);
  408.     tc_cnt++;
  409.     $1 = emph_req_on ∩ hnm ∩ " ";
  410.     $0 ∩= emph_req_off;
  411.     if ( !hl ) $0 ∩= cus_req_off;
  412.     add_line;
  413.     printline(no);
  414.     next;
  415. }
  416.  
  417. GROUP /^\.hr/ {
  418.     if ( NF > 1 ) {
  419.         switch ( $2 ) {
  420.             case on:
  421.                 header = TRUE;
  422.                 break;
  423.             case off:
  424.                 header = FALSE;
  425.                 break;
  426.             default:
  427.                 hdr_line = strim(substr($0,4));
  428.                 break;
  429.         }
  430.     } else hdr_line = dhdr_line;
  431.     next;
  432. }
  433.  
  434. GROUP /^\.fr/ {
  435.     if ( NF > 1 ) {
  436.         switch ( $2 ) {
  437.             case on:
  438.                 footer = TRUE;
  439.                 break;
  440.             case off:
  441.                 footer = FALSE;
  442.                 break;
  443.             default:
  444.                 ftr_line = strim(substr($0,4));
  445.                 break;
  446.         }
  447.     } else ftrline = dftr_line;
  448.     next;
  449. }
  450.  
  451. GROUP /^\.pl/ {   # set page length in lines      # check if 2nd field an integer
  452.     # set page length if TRUE
  453.     if ( $2 ~~ fnum ) page_length = $2;
  454.       else page_length = d_page_length;
  455.     num_lines = page_length - top_margin - bot_margin - (header * (hdr_lines - hdr_skip)) - (footer * (ftr_lines - ftr_skip));
  456.     next;
  457. }
  458.  
  459. GROUP /^\.tm/ { # set top margin
  460.     if ( $2 ~~ fnum ) top_margin = $2;
  461.       else top_margin = d_top_margin;
  462.     num_lines = page_length - top_margin - bot_margin - (header * (hdr_lines - hdr_skip)) - (footer * (ftr_lines - ftr_skip));
  463.     next;
  464. }
  465.  
  466. GROUP /^\.bm/ { # set bottom margin
  467.     if ( $2 ~~ fnum ) bot_margin = $2;
  468.       else bot_margin = d_bot_margin;
  469.     num_lines = page_length - top_margin - bot_margin - (header * (hdr_lines - hdr_skip)) - (footer * (ftr_lines - ftr_skip));
  470.     next;
  471. }
  472.  
  473. GROUP /^\.pa/ {   # page eject
  474.     local num_ejects;
  475.  
  476.     if ( !top ) page_eject;
  477.     if ( $2 ~~ fnum ) {                 # check if second field an integer
  478.         num_ejects = $2 - 1;
  479.         while ( num_ejects-- ) page_eject;  # use 'page_eject' function in
  480.     }                                       # order to get ejected pages
  481.     next;                                   # numbered
  482. }
  483.  
  484. GROUP /^\.pn/ { # page numbering
  485.     if ( NF > 1 ) {
  486.         i = 2;
  487.         do {
  488.             switch ( strlwr($i) ) {
  489.                 case fnum:
  490.                     if ( $i ~~ /^[-+]/ ) page += $i; else page_num = $i;
  491.                     break;
  492.                 case "alpha":
  493.                     alpha_num = TRUE;
  494.                     roman_num = FALSE;
  495.                     break;
  496.                 case "roman":
  497.                     roman_num = TRUE;
  498.                     alpha_num = FALSE;
  499.                     break;
  500.                 case "arabic":
  501.                 case "normal":
  502.                     roman_num = alpha_num = FALSE;
  503.                     break;
  504.             }
  505.         } while ( ++i <= NF );
  506.     } else page_num = 1;
  507.     next;
  508. }
  509.  
  510. GROUP /^\.ds/ {   # double space
  511.     double_space = TRUE;
  512.     next;
  513. }
  514.  
  515.  
  516. GROUP /^\.se/ {   # set variable
  517.     switch ( $2 ) {
  518.         case on:
  519.             var_replace = TRUE; # turn on variable replacement
  520.             break;
  521.         case off:
  522.             var_replace = FALSE; # turn off variable replacement
  523.             break;
  524.         default:
  525.             $1 = "";
  526.             if ( $0 !~ /;$/ ) $0 ∩= ';';
  527.             execute($0);
  528.             break;
  529.     }
  530.     next;
  531. }
  532.  
  533. GROUP /^\.ss/ {   # single space
  534.     double_space = FALSE;
  535.     next;
  536. }
  537.  
  538. GROUP /^\.sp/ {   # line spaces
  539.     local num_spaces = 1;
  540.  
  541.     printline(no);
  542.     if ( $2 ~~ fnum ) num_spaces = $2;  # check if 2nd field an integer
  543.     while ( num_spaces-- ) output_line("",TRUE); # print blank lines for line skips
  544.     next;
  545. }
  546.  
  547. GROUP /^\.sx/ {    # command to split text
  548.     printline(no);
  549.     output_line(split_text(strim(substr($0,4)),width),TRUE);
  550.     cnt = 0;
  551.     next;
  552. }
  553.  
  554. GROUP /^\.tb/ {   # command to tab over nn spaces
  555.     local ol;
  556.  
  557.     printline(no);
  558.     # check if second field an integer
  559.     if ( $2 ~~ fnum ) {
  560.         ol = copies(" ",$2);
  561.         $2 = "";
  562.     }
  563.     $1 = "";
  564.     ol ∩= strim($0);
  565.     output_line(ol,TRUE);
  566.     next;
  567. }
  568.  
  569. # start ordered list
  570. GROUP /^:ol\./ {
  571.     printline(no);
  572.     list_list[++list_level] = 1;
  573.     left_margin = copies(" ",length(left_margin) + 4);
  574.     width -= 4;
  575.     olist++;
  576.     next;
  577. }
  578.  
  579. # start simple list
  580. GROUP /^:sl\./ {
  581.     printline(no);
  582.     list_list[++list_level] = -1;
  583.     left_margin = copies(" ",length(left_margin) + 4);
  584.     width -= 4;
  585.     next;
  586. }
  587.  
  588. # start un-ordered list
  589. GROUP /^:ul\./ {
  590.     printline(no);
  591.     list_list[++list_level] = 0;
  592.     left_margin = copies(" ",length(left_margin) + 4);
  593.     width -= 4;
  594.     ulist++;
  595.     next;
  596. }
  597.  
  598. # list item
  599. GROUP /^:li\./ {
  600.     if ( list_level ) {
  601.         printline(no);
  602.         $0 = strim(substr($0,5));
  603.         list_item;
  604.     }
  605.     next;
  606. }
  607.  
  608. # end [un-]ordered list
  609. GROUP /^:e[osu]l\./ {
  610.     if ( list_level ) {
  611.         printline(no);
  612.         --list_level;
  613.         left_margin = copies(" ",length(left_margin) - 4);
  614.         width += 4;
  615.         list_left_mh = FALSE;
  616.         if ( substr($0,3,1) == 'o' && olist ) olist--;
  617.           else if ( substr($0,3,1) == 'u' && ulist ) ulist--;
  618.     }
  619.     next;
  620. }
  621.  
  622. # table of contents command
  623. # .tc n - n == number of pages to reserve, default == 1
  624. GROUP /^\.tc/ {
  625.     local toc_pages = 1;
  626.  
  627.     if ( !top ) page_eject;
  628.     toc_pnum = page_num;
  629.     if ( NF > 1 && $2 ~~ fnum ) toc_pages = $2;
  630.     page_num += toc_pages;
  631.     next;
  632. }
  633.  
  634. # put a line into table of contents
  635. GROUP /^\.pt/ {
  636.     local r_num = set_pnum(page_num);
  637.  
  638.     toc_lines[tc_cnt]["pgn"] = r_num;
  639.     toc_lines[tc_cnt]["line"] = strim(substr($0,4));
  640.     toc_lines[tc_cnt]["skip"] = FALSE;
  641.     tc_cnt++;
  642.     next;
  643. }
  644.  
  645. # command to define table
  646. # $2 == table separator
  647. # $3 ... $NF == table columns starts
  648. GROUP /^\.td/ {
  649.     local i, j;
  650.  
  651.     t_sep = $2;
  652.     if ( NF > 2 ) {
  653.         deletea t_col;
  654.         for ( i = 3 , j = 1 ; i <= NF ; i++ , j++ ) {
  655.             t_col[j] = $i;
  656.         }
  657.     }
  658.     next;
  659. }
  660.  
  661. # start/stop table formatting
  662. GROUP /^\.tl/ {
  663.     local f2 = strlwr($2);
  664.  
  665.     if ( f2 == cstart ) {
  666.         printline(no);
  667.         table_f = TRUE;
  668.         table_format = format;
  669.         format = FALSE;
  670.       } else if ( f2 == cstop ) {
  671.         table_f = FALSE;
  672.         format = table_format;
  673.       } else {
  674.         fprintf(stderr,"'tl' Command Only Recognizes 'start' or 'stop'.\n");
  675.         exit 3;
  676.     }
  677.     next;
  678. }
  679.  
  680. GROUP /^\.us/ { # underscore command
  681.     local varout;
  682.     local before = FALSE;
  683.     local skip_to_next = NF < 3 ? TRUE : FALSE;
  684.  
  685.     if ( NF == 2 ) {
  686.         switch ( $2 ) {
  687.             default:
  688.                 skip_to_next = FALSE;
  689.                 $1 = $2;
  690.                 $2 = "";
  691.                 NF = 1;
  692.             case on:
  693.                 before = TRUE;
  694.                 if ( !cus_command ) varout = cus_req_on;
  695.                 cus_command = TRUE;
  696.                 break;
  697.             case off:
  698.                 if ( cus_command ) varout = cus_req_off;
  699.                 cus_command = FALSE;
  700.                 break;
  701.         }
  702.       } else {
  703.         before = TRUE;
  704.         if ( !cus_command ) varout = cus_req_on;
  705.         if ( NF == 1 ) ptr_one_line = cus_req_off;
  706.         $1 = "";
  707.     }
  708.     if ( before ) printer_command_before ∩= varout;
  709.       else printer_command_after ∩= varout;
  710.     width_cnt++;
  711.     width++;
  712.     if ( skip_to_next ) next;
  713. }
  714.  
  715.     {
  716.     if ( format ) add_line;
  717.       else if ( table_f ) format_table($0);
  718.       else output_line($0,TRUE);
  719. }
  720.  
  721. # end of file - clean up between files
  722. FINAL {
  723.     local dummy;
  724.  
  725.     if ( !top ) page_eject;
  726.     imbedded_file = FALSE;
  727.     if ( ad_line ) {
  728.         fprintf(stderr,"Print Envelope (y/n): ");
  729.         dummy = fgetc(keyboard);
  730.         fprintf(stderr,"\n");
  731.         if ( tolower(dummy) == "y" ) {
  732.             fprintf(stderr,"Place Envelope in Printer\nPress Enter");
  733.             fgetline(stdin,dummy);
  734.             for ( sr_line in sender ) printf(" %s\n",sender[sr_line]);
  735.             printf(copies('\n',8));
  736.             for ( ad_line in addressee ) printf("\t\t\t%s\n",addressee[ad_line]);
  737.             printf("\f");
  738.             sr_line = FALSE;
  739.             ad_line = FALSE;
  740.         }
  741.     }
  742. }
  743.  
  744. # end of file - clean up and eject last page
  745. END {   # use for single document
  746.     if ( toc_pnum ) print_toc;
  747.     if ( !top ) page_eject;
  748. }
  749.  
  750. # function to add current line to parsed text
  751. function add_line() {
  752.     for ( i = 1 ; i <= NF ; i++ ) if ( length($i) ) addword($i);
  753. }
  754.  
  755. # output accumulated words
  756. function output_line(oline,leftm) {
  757.     if ( top ) print_top;
  758.     if ( leftm ) printf(left_margin);
  759.     if ( list_left_mh ) {
  760.         left_margin = list_left_mh;
  761.         list_left_mh = FALSE;
  762.     }
  763.     if ( ptr_one_line ) {
  764.         oline ∩= ptr_one_line;
  765.         ptr_one_line = FALSE;
  766.     }
  767. # translate required blank characters to blank
  768. # gsub version using regular expression
  769. #    gsub(req_be," ",oline);
  770. #    print oline;
  771. # stran version using strings
  772.     oline = stran(oline," ",req_bs);
  773. # translate required characters to printer control codes
  774. # gsub version using regular expression
  775.     gsub(cus_req_on  , paper_output ? cus_on   : "" ,oline);
  776.     gsub(cus_req_off , paper_output ? cus_off  : "" ,oline);
  777.     gsub(emph_req_on , paper_output ? emph_on  : "" ,oline);
  778.     gsub(emph_req_off, paper_output ? emph_off : "" ,oline);
  779.     print oline;
  780.     line_cnt += 1 + double_space;   # increment line counter
  781.     if ( paginate && line_cnt >= num_lines )    # check for end of page
  782.         end_page;   # function to print page numbers - note absence
  783.                     # of '()' to invoke
  784.     else if ( double_space ) print "";
  785. }
  786.  
  787. # accumlate words for line
  788. function addword(w) {
  789.     local lw = length(w);   # length of added word
  790.  
  791.     if ( double_blank && w ~~ end_of_sentence ) { # check for end of sentence
  792.         lw++;
  793.         w ∩= req_bs; # add extra blank at end of sentence
  794.     }
  795.     if ( cnt + size + lw > width ) {    # check new line length
  796.         # Following statement removes possible extra blank at end of sentence
  797.         # Do not want if sentence ends line
  798. #        if ( double_blank ) sub(remove_trail,"",line[cnt]);
  799.         if ( double_blank ) line[cnt] = strim(line[cnt],FALSE,req_bs);
  800.         printline(yes);
  801.     }
  802.     if ( printer_command_before ) {
  803.         w = printer_command_before ∩ w;
  804.         printer_command_before = "";
  805.     }
  806.     if ( printer_command_after ) {
  807.         w ∩= printer_command_after;
  808.         printer_command_after = "";
  809.     }
  810.     line[++cnt] = w;    # add word to line array
  811.     size += lw;
  812. }
  813.  
  814. # print accumulated words in justified line
  815. function printline(f) {
  816.     local i, nb, nsp, holes;
  817.     local tline;
  818.     local adj = adjust + cnt;
  819.     local fmt = format;
  820.  
  821.     if ( cnt ) {
  822.         if ( para_start && para_indent ) line[1] = copies(" ",para_indent) ∩ line[1];
  823.         if ( strlwr(f) == no && size  < (width - adj) ) fmt = fmt < right_justify ? left_justify : fmt;
  824.         tline = form_line(line,cnt,width,fmt);
  825.         output_line(tline,TRUE);
  826.         if ( set_width ) {
  827.             width = set_width;
  828.             set_width = FALSE;
  829.         }
  830.         size = cnt = 0;
  831.         if ( width_cnt ) {
  832.             width -= width_cnt;
  833.             width_cnt = 0;
  834.         }
  835.         deletea line;
  836.     }
  837.     if ( para_start ) para_start = FALSE;
  838. }
  839.  
  840. # eject current page - print blank lines to bottom,
  841. # print page number and form feed
  842. function page_eject() {
  843.     printline(no);
  844.     while ( ++line_cnt <= num_lines ) print "";
  845.     end_page;
  846. }
  847.  
  848. # end of page clean up - print page number & eject
  849. function end_page() {
  850.     local pnl = replace(ftr_line);      # replace defined variables in footer
  851.     local r_num = set_pnum(page_num);
  852.     local left_margin = copies(" ",d_left_m);
  853.     local ftrs = ftr_skip;
  854.  
  855.     if ( footer ) {
  856.         while ( ftrs-- ) print "";
  857.         sub(page_num_sym,r_num,pnl);    # substitute for page number
  858.           # print left margin and footer text split across default page width
  859.         print left_margin ∩ split_text(pnl,d_width);
  860.           # use following line if want text centered instead of split
  861. #        print left_margin ∩ center(pnl,width);
  862.     }
  863.     printf("\f");
  864.     line_cnt = 0;
  865.     top = TRUE;
  866.     if ( wait_on_page ) {   # if waiting at end of page - display message, wait
  867.         fprintf(stderr,"Insert New Page, Press Carriage Return\n");
  868.         fgetline(stdin,pnl);
  869.     }
  870.     fprintf(stderr,"Finished Page: %s (%u)\n",set_pnum(page_num),ttl_page_cnt);
  871.     page_num++;
  872.     ttl_page_cnt++;
  873. }
  874.  
  875. # print top margin and header if any
  876. function print_top() {
  877.     local hdr_lne = replace(hdr_line);  # replace defined variables in header
  878.     local r_num = set_pnum(page_num);
  879.     local left_margin = copies(" ",d_left_m);
  880.     local hdrs = hdr_skip;
  881.  
  882.     top = FALSE;
  883.     # print top margin
  884.     while ( ++line_cnt <= top_margin ) print "";
  885.     if ( header ) {
  886.         sub(page_num_sym, r_num , hdr_lne );  # substitute for page number
  887.           # print left margin and header text split across default page width
  888.         print left_margin ∩ split_text(hdr_lne,d_width);
  889.           # use following line if want text centered instead of split
  890. #        print left_margin ∩ center(hdr_lne,width);
  891.         while ( hdrs-- ) { print ""; line_cnt++; }
  892.     }
  893. }
  894.  
  895. # function to convert decimal number to roman numeral
  896. # used if roman numeral page numbering desired - see roman_num variable
  897. function roman_numeral(num) {
  898.       # pwr_ten ==> maximum integer representable as roman numeral
  899.     local i, pwr_ten = 10000;
  900.     local frst_sym = '?', secd_sym = 'm', thrd_sym = '?';
  901.     local num_str = "";
  902.     local three_sym;
  903.  
  904.     num = int(num);
  905.     if ( num <= pwr_ten )
  906.         do {
  907.             pwr_ten /= 10;
  908.             i = num / pwr_ten;
  909.             num -= i * pwr_ten;
  910.             three_sym = "";
  911.             switch ( i ) {
  912.                 case 3:
  913.                     num_str ∩= secd_sym;
  914.                 case 2:
  915.                     num_str ∩= secd_sym;
  916.                 case 1:
  917.                     num_str ∩= secd_sym;
  918.                     break;
  919.                 case 4:
  920.                     num_str ∩= secd_sym;
  921.                     num_str ∩= thrd_sym;
  922.                     break;
  923.                 case 5:
  924.                     num_str ∩= thrd_sym;
  925.                     break;
  926.                 case 8:
  927.                     three_sym ∩= secd_sym;
  928.                 case 7:
  929.                     three_sym ∩= secd_sym;
  930.                 case 6:
  931.                     three_sym ∩= secd_sym;
  932.                     num_str ∩= thrd_sym three_sym;
  933.                     break;
  934.                 case 9:
  935.                     num_str ∩= secd_sym;
  936.                     num_str ∩= frst_sym;
  937.                     break;
  938.                 case 0:
  939.                     break;
  940.             }
  941.             switch ( frst_sym = secd_sym ) {
  942.                 case 'm':
  943.                     secd_sym = 'c';
  944.                     thrd_sym = 'd';
  945.                     break;
  946.                 case 'c':
  947.                     secd_sym = 'x';
  948.                     thrd_sym = 'l';
  949.                     break;
  950.                 case 'x':
  951.                     secd_sym = 'i';
  952.                     thrd_sym = 'v';
  953.                     break;
  954.             }
  955.         } while ( pwr_ten && num );
  956.     return num_str;
  957. }
  958.  
  959. # convert decimal number to alphabetic number - useful if alphabetic page
  960. # numbering wanted
  961. function d_to_alpha(num) {
  962.     local num_str = "";
  963.     local k;
  964.     local alphabet = "abcdefghijklmnopqrstuvwxyz";
  965.  
  966.     num = int(num);
  967.     if ( num > 0 && num <= 308915776 ) { # test for maximum number convertable
  968.         while ( num > 26 ) {
  969.             k = num % 26;
  970.             num /= 26;
  971.             if ( !k ) {
  972.                 k = 26;
  973.                 num--;
  974.             }
  975.             num_str = substr(alphabet,k,1) ∩ num_str;
  976.         }
  977.         num_str = substr(alphabet,num,1) ∩ num_str;
  978.     }
  979.     return num_str;
  980. }
  981.  
  982. # function to split text '.sx' control word
  983. function split_text(text,line_width) {
  984.     local cnt;
  985.     local sx_line, s_line;
  986.     local delim = substr(text,1,1);
  987.  
  988.       # split line on delimitor
  989.     cnt = split(text,s_line,delim);
  990.       # check for three fields
  991.     if ( cnt == 3 ) {
  992.           # center center portion in 'line_width'
  993.         sx_line = center(s_line[2],line_width,stx_chr);
  994.           # overlay left portion
  995.         sx_line = overlay(sx_line,s_line[1],1);
  996.           # overlay right portion
  997.         sx_line = overlay(sx_line,s_line[3],line_width - length(s_line[3]) + 1);
  998.       } else {
  999.         fprintf(stderr,"'.sx' Command Needs 3 Fields.\nLine: %d\nFile: %s\n",FNR,FILENAME);
  1000.         exit 1;
  1001.     }
  1002.     return sx_line;
  1003. }
  1004.  
  1005. # function to format input lines into table columns
  1006. function format_table(iline) {
  1007.     local oline, ocnt;
  1008.     local cline, ccnt;
  1009.     local pline = copies(" ",width);
  1010.     local i, j;
  1011.  
  1012.     ocnt = split(iline,oline,t_sep);    # split input line into columns
  1013.     for ( i in t_col ) {
  1014.         if ( i > ocnt ) break;
  1015.         ccnt = split(oline[i],cline,/{_w}+/); # split column into words
  1016.         pline = overlay(pline,form_line(cline,ccnt,width,left_justify),t_col[i]);
  1017.     }
  1018.     output_line(pline,TRUE);
  1019. }
  1020.  
  1021. # function to output list item
  1022. function list_item() {
  1023.     local lmrgn = length(left_margin);
  1024.     local lsth;
  1025.     local bchr, bhdr, bnum;
  1026.     local blnk_sep = copies(req_bs,2);
  1027.     local list_item_hdr;    # Last List Item Header String
  1028.  
  1029.     list_left_mh = left_margin;
  1030.     left_margin = copies(" ",lmrgn - 4);
  1031.     if ( list_list[list_level] > 0 ) { # ordered list
  1032.         lsth = lstb[olist % olstn] ∩ blnk_sep;
  1033.           # use numeric for odd levels, alpha for even levels
  1034.         if ( olist & 1 ) list_item_hdr = list_list[list_level]++ ∩ lsth;
  1035.           else list_item_hdr = d_to_alpha(list_list[list_level]++) ∩ lsth;
  1036.       } else if ( !list_list[list_level] ) { # un-ordered list
  1037.           # get proper bullet for odd/even list level
  1038.         if ( ulist & 1 ) bchr = blst_odd; else bchr = blst_even;
  1039. #        for ( bnum = int((ulist + 1) / 2) ; bnum ; bnum-- ) bhdr = bchr ∩ bhdr;
  1040.         for ( bnum = int((ulist + 1) / 2) ; bnum ; bnum-- ) bhdr ∩= bchr;
  1041.         list_item_hdr = bhdr ∩ blnk_sep;
  1042.     } else list_item_hdr = "";
  1043.     $1 = list_item_hdr ∩ $1;
  1044.     set_width = width;
  1045.     width += 4;
  1046.     add_line;
  1047. }
  1048.  
  1049. # function to convert year/month/day into julian day number
  1050. ##include <jdn.exp>
  1051.  
  1052. # form array of words into output line
  1053. # line ==> array of words
  1054. # cnt ==>  number of elements in line
  1055. # width ==> character width of line to form
  1056. # format == 1 justify on width
  1057. # format == right_justify || left justify
  1058. function form_line(line,cnt,width,format) {
  1059.     local i, oline;
  1060.  
  1061.     if ( format > TRUE ) {
  1062.         for ( i = 2 , oline = line[1] ; i <= cnt ; i++ ) oline ∩= " " ∩ line[i];
  1063.         if ( format == right_justify )
  1064.           oline = copies(" ",width - length(oline)) ∩ oline;
  1065.     } else oline = justify(line,cnt,width);
  1066.     return oline;
  1067. }
  1068.  
  1069. function print_toc() {
  1070.     local tline;
  1071.     local tcnt;
  1072.     local stx_chr_h = stx_chr;
  1073.     local hld_hdr = hdr_line;
  1074.     local hld_ftr = ftr_line;
  1075.     local prefix_hld = page_num_prefix;
  1076.  
  1077.     if ( !top ) page_eject;
  1078.     hdr_line = toc_hdr_line;
  1079.     ftr_line = toc_ftr_line;
  1080.     page_num_prefix = "";
  1081.     alpha_num = FALSE;
  1082.     roman_num = TRUE;
  1083.     page_num = toc_pnum;
  1084.     output_line("Table of Contents",TRUE);
  1085.     output_line("",TRUE);
  1086.     for ( tcnt = 0 ; tcnt < tc_cnt ; tcnt++ ) {
  1087.         if ( toc_lines[tcnt]["skip"] ) output_line("",TRUE);
  1088.         tline = '\x0ff' ∩ toc_lines[tcnt]["line"] ∩ " " ∩ "\x0ff\x0ff" ∩ " " ∩ toc_lines[tcnt]["pgn"] ∩ '\x0ff';
  1089.         stx_chr = '.';
  1090.         tline = split_text(tline,d_width);
  1091.         stx_chr = stx_chr_h;
  1092.         output_line(tline,TRUE);
  1093.     }
  1094.     if ( !top ) page_eject;
  1095.     hdr_line = hld_hdr;
  1096.     ftr_line = hld_ftr;
  1097.     page_num_prefix = prefix_hld;
  1098. }
  1099.  
  1100. # return page number aaccording to style set
  1101. function set_pnum(pnum) {
  1102.     local r_num = roman_num ? roman_numeral(pnum) : alpha_num ? d_to_alpha(pnum) : pnum;
  1103.  
  1104.     return replace(page_num_prefix) ∩ r_num;
  1105. }
  1106.