home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / qtawk / fmtdoc2p.exp < prev    next >
Text File  |  1990-04-29  |  28KB  |  993 lines

  1. # QTAwk program to format ASCII text files
  2. # (C) Copyright 1990 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.     olstn = 10;
  89.     d_page_length = page_length = 66;    # page length at 6 lines per inch
  90.     d_top_margin = top_margin = 6;     # number of lines reserved for top margin
  91.     d_bot_margin = bot_margin = 6;     # number of lines reserved for bottom margin
  92.     d_width = 65;    # default width of text == line length
  93.     width = 65;     # width of text == line length
  94.     d_left_m = 8;    # default number of blanks on left margin
  95.     left_m = 8;     # number of blanks on left margin
  96.     header = FALSE;
  97.     footer = TRUE;
  98.     hdr_line = dhdr_line = "||- # -||";   # header line
  99.     ftr_line = dftr_line = "||- #/{totl_cnt} -||";   # footer line
  100.     toc_hdr_line = "|||Table of Contents|";   # header line
  101.     toc_ftr_line = dftr_line;    # Footer line
  102.     hdr_lines = 1;    # number of lines in page header
  103.     hdr_skip = 2;    # number of blank lines after header line(s)
  104.     ftr_lines = 1;    # number of lines in page footer
  105.     ftr_skip = 2;    # number of blank lines before footer line(s)
  106.     # default number of text body lines/page
  107.     # == 54 lines default
  108.     num_lines = page_length - top_margin - bot_margin - (header * (hdr_lines - hdr_skip)) - (footer * (ftr_lines - ftr_skip));
  109.     ttl_page_cnt = 1;    # indexpendent count of pages
  110.     page_num = 1;    # page number
  111.     page_num_prefix = "";   # page number prefix string
  112.     reset_headr_0 = FALSE;  # flag to set page number to 1 on ".ho" command
  113.     left_margin = copies(" ",left_m); # left margin blanks
  114.     double_space = FALSE;   # double spacing output
  115.     roman_num = FALSE;    # page numbering in arabic or roman
  116.     alpha_num = FALSE;    # page numbering in alphabetic
  117.     adjust = 5; # justify lines if size within adjust of width
  118.     wait_on_page = FALSE;   # TRUE if wait at end of page
  119.     para_indent = 1;    # number of blanks less one - first
  120.             # line of para. indented
  121.     para_start = FALSE; # flag whether line starts new para.
  122.     format = TRUE;    # format flag
  123.     right_justify = 10; # format value to right justify text
  124.     left_justify  = 20; # format value to left    justify text
  125.     table_format = format;# variable to hold format value while doing table
  126.     var_replace = TRUE; # default to replace variables
  127.     req_be = /\x0ff/; # required blank - tranlated to blank just prior to output
  128.             # above regular expression version
  129.     req_bs = '\x0ff'; # required blank - tranlated to blank just prior to output
  130.             # above string version
  131.             # need both regular expression version and string version
  132.             # to get speed of r.e. search and replacement
  133.             # and string to place hexadecimal character in output
  134.     split(sdate(2),date,/\//);
  135.     sysyear = date[3];
  136.     sysmonth = date[2];
  137.     sysdayofm = date[1];
  138.     sysdayofw = ((jdn(sysyear,sysmonth,sysdayofm) + 1) % 7) + 1;
  139.     sysdayofy = sdate(16);
  140.     date1 = sdate(1);     # set today's date variable
  141.     date2 = sdate(2);     # set today's date variable
  142.     date3 = sdate(3);     # set today's date variable
  143.     date4 = sdate(4);     # set today's date variable
  144.     date5 = sdate(5);     # set today's date variable
  145.     date6 = sdate(6);     # set today's date variable
  146.     date7 = sdate(7);     # set today's date variable
  147.     date8 = sdate(8);     # set today's date variable
  148.     date9 = sdate(9);     # set today's date variable
  149.     date = date9;
  150.     time = stime(4);    # set document format time
  151.     fnum = /^{_i}$/;    # regular expression for recognizing numeric fields
  152.     stx_chr = ' ';      # character used by split_text function for mask line
  153.     pass = 1;
  154.     totl_cnt = 0;
  155.     stderr = "stderr";
  156.     stdin = "stdin";
  157.     cstart = "start";
  158.     cstop = "stop";
  159.     no = "no";
  160.     yes = "yes";
  161.     on = "on";
  162.     off = "off";
  163.     right = "right";
  164.     left = "left";
  165.  
  166.     hnum = major_sctn = minor_sctn = 0;
  167.     imbedded_file = FALSE;
  168.     tc_cnt = 0;
  169.     outf = "nul";
  170. #
  171. # following variables define printer control sequences
  172. # currently set for IBM Proprinter
  173. #
  174. #   turn emphasized printing on/on
  175.     emph_on  = "\x01b\x045";
  176.     emph_off = "\x01b\x046";
  177. # Continuous Underscore on/off
  178.     cus_on  = "\x01b-\x001";
  179.     cus_off = "\x01b-\x080";
  180. # Condensed Print on/off
  181.     condsd_on  = '\x00f';
  182.     condsd_off = '\x012';
  183. # start/stop Letter Quality Print - proprinter
  184.     lttrq_on  = "\x01bI\x002";
  185.     lttrq_off = "\x01bI\x080";
  186. # start/stop Letter Quality Print - color printer
  187.     lttrq_on_cp  = "\x01bI\x003";
  188.     lttrq_off_cp = "\x01bI\x002";
  189. # sub/super-script
  190.     sub_on = "\x01bS\x001";
  191.     sup_on = "\x01bS\x080";
  192. # Cancel sub/super-script Printing
  193.     sub_sup_off = "\x01bT";
  194. # set ribbon color - color printer
  195.     band_1 = "\x01by";      # yellow
  196.     band_2 = "\x01bm";      # red
  197.     band_3 = "\x01bc";      # blue
  198.     band_4 = "\x01bb";      # black
  199. #
  200. # printer initialization strings would be printed here, e.g.,
  201. # to turn on built-in letter quality print, the following
  202. # print statement is used:
  203.     printf(lttrq_on);
  204. #
  205. # intialize printing - print header blank lines
  206. # use in BEGIN     actions for single document from many
  207. # use in INITIAL actions for many single documents
  208. #
  209. #}    # remove comment at line beginning for many documents
  210. # INITIAL { # remove comment at line beginning for many documents
  211.     top = TRUE;
  212. }
  213.  
  214.  
  215. GROUP /^{_w}*$/ { # blank input line - allow for possible blanks and tabs
  216.     printline(no);
  217.     if ( !top ) output_line("",TRUE);   # do not output blanks if at top of page
  218.     para_start = TRUE;
  219.     size += para_indent;
  220.     next;
  221. }
  222.  
  223. GROUP /^\.\*/ {   # comment line - ignore
  224.     next;
  225. }
  226.  
  227. # replace defined variables in format {named_variable} in input record
  228.     {
  229.     if ( var_replace ) $0 = replace($0); # replace variables
  230. }
  231.  
  232. GROUP /^\.sr/ {   # set from addressee
  233.     sender[sr_line++] = strim(substr($0,4));
  234.     $1 = ".tb 35";
  235.     cycle;
  236. }
  237.  
  238. GROUP /^\.ad/ {   # format addressee
  239.     addressee[ad_line++] = strim(substr($0,4));
  240.     $1 = ".br";
  241.     cycle;
  242. }
  243.  
  244. GROUP /^\.fo/ {   # format on/off command
  245.     switch ( $2 ) {
  246.     case on:
  247.         format = TRUE;
  248.         break;
  249.     case off:
  250.         format = FALSE;
  251.         break;
  252.     case right:
  253.         format = right_justify;
  254.         break;
  255.     case left:
  256.         format = left_justify;
  257.         break;
  258.     default:
  259.         fprintf(stderr,"Incorrect format '.fo' command.\nFile: %s\nLine: %u\n",FILENAME,FNR);
  260.         exit 2;
  261.         break;
  262.     }
  263.     printline(no);
  264.     next;
  265. }
  266.  
  267. GROUP /^\.br/ { # break command
  268.     printline(no);
  269.     if ( NF > 1 ) {
  270.     $0 = strim(substr($0,4));
  271.     add_line;
  272.     }
  273.     next;
  274. }
  275.  
  276. GROUP /^\.ce/ {   # command to center line
  277.     local tcnt, tline;
  278.  
  279.     printline(no);
  280.     tcnt = split(strim(substr($0,4)),tline);
  281.     tline = form_line(tline,tcnt,width,left_justify);
  282.     output_line(center(tline,width),TRUE);
  283.     next;
  284. }
  285.  
  286. GROUP /^\.il/ {   # indent next line n spaces
  287.     printline(no);
  288.     if ( $2 ~~ fnum )            # check if 2nd field an integer
  289.       addword(copies(" ",$2));
  290.     next;
  291. }
  292.  
  293. GROUP /^\.im/ {
  294.     if ( !imbedded_file ) {
  295.     ARGI --;
  296.     FILENAME = $2;
  297.     imbedded_file = TRUE;
  298.     fprintf(stderr,"Imbedded File: %s [Page: %s (%u)\n",FILENAME,set_pnum(page_num),ttl_page_cnt);
  299.       } else {
  300.     fprintf(stderr,"Only One Level Of File Imbedding Allowed.\nFile: %s\nLine: %u\n",FILENAME,FNR);
  301.     exit 200;
  302.     }
  303.     next;
  304. }
  305.  
  306. GROUP /^\.in/ {   # set indentation
  307.     if ( $2 ~~ fnum ) {
  308.     if ( $2 ~~ /^[+-]/ ) {
  309.         left_m += $2;
  310.         if ( left_m < 0 ) left_m = d_left_m;
  311.     } else left_m = $2;
  312.     } else left_m = d_left_m;
  313.     left_margin = copies(" ",left_m);
  314.     next;
  315. }
  316.  
  317. GROUP /^\.ll/ {   # set line length
  318.     if ( $2 ~~ fnum ) width = $2;    # check if 2nd field an integer
  319.       else width = d_width;            # set line length if TRUE
  320.     next;
  321. }
  322.  
  323. GROUP /^\.h[0-9]/ {
  324.     local hl, i, hnm;
  325.  
  326.     printline(no);
  327.     hl = int(substr($$0,3,1));
  328.     if ( hl < head_level ) {
  329.     for ( i = hl + 1 ; i < 10 ; i++ ) hla[i] = 0;
  330.       } else if ( hl > head_level + 1 ) {
  331.     fprintf(stderr,"Error - Incorrect Head Level Specified.\nInput File: %s\nRecord Number: %u",FILENAME,FNR);
  332.     exit 10;
  333.     }
  334.     if ( !(head_level = hl) ) {
  335.     if ( !top ) page_eject;
  336.     if ( !(page_num & 1) ) page_eject;
  337.     if ( reset_headr_0 ) page_num = 1;
  338.     }
  339.     ++hla[hl];
  340.     hnum = hla[0] "";
  341.     if ( hl ) {
  342.     minor_sctn = hla[hl];
  343.     for ( i = 1 ; i <= hl ; i++ ) hnum ∩= "." hla[i];
  344.     hnm = hnum;
  345.       } else {
  346.     major_sctn++;
  347.     minor_sctn = 0;
  348.     hnum ∩= ".0";
  349.     hnm = cus_on ∩ hnum;
  350.     toc_lines[tc_cnt]["skip"] = TRUE;
  351.     }
  352.     $1 = hnum;
  353.     toc_lines[tc_cnt]["line"] = $0;
  354.     toc_lines[tc_cnt]["pgn"] = set_pnum(page_num);
  355.     tc_cnt++;
  356.     $1 = emph_on ∩ hnm ∩ " ";
  357.     $0 ∩= emph_off;
  358.     if ( !hl ) $0 ∩= cus_off;
  359.     add_line;
  360.     printline(no);
  361.     next;
  362. }
  363.  
  364. GROUP /^\.hr/ {
  365.     if ( NF > 1 ) {
  366.     switch ( $2 ) {
  367.         case on:
  368.         header = TRUE;
  369.         break;
  370.         case off:
  371.         header = FALSE;
  372.         break;
  373.         default:
  374.         hdr_line = strim(substr($0,4));
  375.         break;
  376.     }
  377.     } else hdr_line = dhdr_line;
  378.     next;
  379. }
  380.  
  381. GROUP /^\.fr/ {
  382.     if ( NF > 1 ) {
  383.     switch ( $2 ) {
  384.         case on:
  385.         footer = TRUE;
  386.         break;
  387.         case off:
  388.         footer = FALSE;
  389.         break;
  390.         default:
  391.         ftr_line = strim(substr($0,4));
  392.         break;
  393.     }
  394.     } else ftrline = dftr_line;
  395.     next;
  396. }
  397.  
  398. GROUP /^\.pl/ {   # set page length in lines      # check if 2nd field an integer
  399.     # set page length if TRUE
  400.     if ( $2 ~~ fnum ) page_length = $2;
  401.       else page_length = d_page_length;
  402.     num_lines = page_length - top_margin - bot_margin - (header * (hdr_lines - hdr_skip)) - (footer * (ftr_lines - ftr_skip));
  403.     next;
  404. }
  405.  
  406. GROUP /^\.tm/ { # set top margin
  407.     if ( $2 ~~ fnum ) top_margin = $2;
  408.       else top_margin = d_top_margin;
  409.     num_lines = page_length - top_margin - bot_margin - (header * (hdr_lines - hdr_skip)) - (footer * (ftr_lines - ftr_skip));
  410.     next;
  411. }
  412.  
  413. GROUP /^\.bm/ { # set bottom margin
  414.     if ( $2 ~~ fnum ) bot_margin = $2;
  415.       else bot_margin = d_bot_margin;
  416.     num_lines = page_length - top_margin - bot_margin - (header * (hdr_lines - hdr_skip)) - (footer * (ftr_lines - ftr_skip));
  417.     next;
  418. }
  419.  
  420. GROUP /^\.pa/ {   # page eject
  421.     local num_ejects;
  422.  
  423.     if ( !top ) page_eject;
  424.     if ( $2 ~~ fnum ) {         # check if second field an integer
  425.     num_ejects = $2 - 1;
  426.     while ( num_ejects-- ) page_eject;  # use 'page_eject' function in
  427.     }                        # order to get ejected pages
  428.     next;                    # numbered
  429. }
  430.  
  431. GROUP /^\.pn/ { # page numbering
  432.     if ( NF > 1 ) {
  433.     i = 2;
  434.     do {
  435.         switch ( strlwr($i) ) {
  436.         case fnum:
  437.             if ( $i ~~ /^[-+]/ ) page += $i; else page_num = $i;
  438.             break;
  439.         case "alpha":
  440.             alpha_num = TRUE;
  441.             roman_num = FALSE;
  442.             break;
  443.         case "roman":
  444.             roman_num = TRUE;
  445.             alpha_num = FALSE;
  446.             break;
  447.         case "arabic":
  448.         case "normal":
  449.             roman_num = alpha_num = FALSE;
  450.             break;
  451.         }
  452.     } while ( ++i <= NF );
  453.     } else page_num = 1;
  454.     next;
  455. }
  456.  
  457. GROUP /^\.ds/ {   # double space
  458.     double_space = TRUE;
  459.     next;
  460. }
  461.  
  462.  
  463. GROUP /^\.se/ {   # set variable
  464.     switch ( $2 ) {
  465.     case on:
  466.         var_replace = TRUE; # turn on variable replacement
  467.         break;
  468.     case off:
  469.         var_replace = FALSE; # turn off variable replacement
  470.         break;
  471.     default:
  472.         $1 = "";
  473.         if ( $0 !~ /;$/ ) $0 ∩= ';';
  474.         execute($0);
  475.         break;
  476.     }
  477.     next;
  478. }
  479.  
  480. GROUP /^\.ss/ {   # single space
  481.     double_space = FALSE;
  482.     next;
  483. }
  484.  
  485. GROUP /^\.sp/ {   # line spaces
  486.     local num_spaces = 1;
  487.  
  488.     printline(no);
  489.     if ( $2 ~~ fnum ) num_spaces = $2;    # check if 2nd field an integer
  490.     while ( num_spaces-- ) output_line("",TRUE); # print blank lines for line skips
  491.     next;
  492. }
  493.  
  494. GROUP /^\.sx/ {    # command to split text
  495.     printline(no);
  496.     output_line(split_text(strim(substr($0,4)),width),TRUE);
  497.     cnt = 0;
  498.     next;
  499. }
  500.  
  501. GROUP /^\.tb/ {   # command to tab over nn spaces
  502.     local ol;
  503.  
  504.     printline(no);
  505.     # check if second field an integer
  506.     if ( $2 ~~ fnum ) {
  507.     ol = copies(" ",$2);
  508.     $2 = "";
  509.     }
  510.     $1 = "";
  511.     ol ∩= strim($0);
  512.     output_line(ol,TRUE);
  513.     next;
  514. }
  515.  
  516. # start ordered list
  517. GROUP /^:ol\./ {
  518.     printline(no);
  519.     list_list[++list_level] = 1;
  520.     left_margin = copies(" ",length(left_margin) + 4);
  521.     width -= 4;
  522.     olist++;
  523.     next;
  524. }
  525.  
  526. # start simple list
  527. GROUP /^:sl\./ {
  528.     printline(no);
  529.     list_list[++list_level] = -1;
  530.     left_margin = copies(" ",length(left_margin) + 4);
  531.     width -= 4;
  532.     next;
  533. }
  534.  
  535. # start un-ordered list
  536. GROUP /^:ul\./ {
  537.     printline(no);
  538.     list_list[++list_level] = 0;
  539.     left_margin = copies(" ",length(left_margin) + 4);
  540.     width -= 4;
  541.     ulist++;
  542.     next;
  543. }
  544.  
  545. # list item
  546. GROUP /^:li\./ {
  547.     if ( list_level ) {
  548.     printline(no);
  549.     $0 = strim(substr($0,5));
  550.     list_item;
  551.     }
  552.     next;
  553. }
  554.  
  555. # end [un-]ordered list
  556. GROUP /^:e[osu]l\./ {
  557.     if ( list_level ) {
  558.     printline(no);
  559.     --list_level;
  560.     left_margin = copies(" ",length(left_margin) - 4);
  561.     width += 4;
  562.     list_left_mh = FALSE;
  563.     if ( substr($0,3,1) == 'o' && olist ) olist--;
  564.       else if ( substr($0,3,1) == 'u' && ulist ) ulist--;
  565.     }
  566.     next;
  567. }
  568.  
  569. # table of contents command
  570. # .tc n - n == number of pages to reserve, default == 1
  571. GROUP /^\.tc/ {
  572.     local toc_pages = 1;
  573.  
  574.     if ( !top ) page_eject;
  575.     toc_pnum = page_num;
  576.     if ( NF > 1 && $2 ~~ fnum ) toc_pages = $2;
  577.     page_num += toc_pages;
  578.     next;
  579. }
  580.  
  581. # put a line into table of contents
  582. GROUP /^\.pt/ {
  583.     local r_num = set_pnum(page_num);
  584.  
  585.     toc_lines[tc_cnt]["pgn"] = r_num;
  586.     $0 = strim(substr($0,4));
  587.     toc_lines[tc_cnt]["line"] = $0;
  588.     toc_lines[tc_cnt]["skip"] = FALSE;
  589.     tc_cnt++;
  590.     next;
  591. }
  592.  
  593. # command to define table
  594. # $2 == table separator
  595. # $3 ... $NF == table columns starts
  596. GROUP /^\.td/ {
  597.     local i, j;
  598.  
  599.     t_sep = $2;
  600.     if ( NF > 2 ) {
  601.     deletea t_col;
  602.     for ( i = 3 , j = 1 ; i <= NF ; i++ , j++ ) {
  603.         t_col[j] = $i;
  604.     }
  605.     }
  606.     next;
  607. }
  608.  
  609. # start/stop table formatting
  610. GROUP /^\.tl/ {
  611.     local f2 = strlwr($2);
  612.  
  613.     if ( f2 == cstart ) {
  614.     printline(no);
  615.     table_f = TRUE;
  616.     table_format = format;
  617.     format = FALSE;
  618.       } else if ( f2 == cstop ) {
  619.     table_f = FALSE;
  620.     format = table_format;
  621.       } else {
  622.     fprintf(stderr,"'tl' Command Only Recognizes 'start' or 'stop'.\n");
  623.     exit 3;
  624.     }
  625.     next;
  626. }
  627.  
  628.     {
  629.     if ( format ) add_line;
  630.       else if ( table_f ) format_table($0);
  631.       else output_line($0,TRUE);
  632. }
  633.  
  634. # end of file - clean up between files
  635. FINAL {
  636.     local dummy;
  637.  
  638.     imbedded_file = FALSE;
  639.     if ( pass == 1 && ARGI == ARGC ) {
  640.     ARGI = 1;
  641.     pass++;
  642.     if ( !top ) page_eject;
  643.     ttl_page_cnt = 1;   # indexpendent count of pages
  644.     totl_cnt = page_num - 1;
  645.     page_num = 1;
  646.     alpha_num = roman_num = FALSE;
  647.     outf = "stdout";
  648.     }
  649. }
  650.  
  651. # end of file - clean up and eject last page
  652. END {    # use for single document
  653.     if ( toc_pnum ) print_toc;
  654.     if ( !top ) page_eject;
  655.     if ( ad_line ) {
  656. #     printline(no);
  657.     if ( !top ) page_eject;
  658.     fprintf(stderr,"Place Envelope in Printer\nPress Enter");
  659.     fgetline(stdin,dummy);
  660.     for ( sr_line in sender ) printf(" %s\n",sender[sr_line]);
  661.     printf(copies('\n',8));
  662.     for ( ad_line in addressee ) printf("\t\t\t\t\t%s\n",addressee[ad_line]);
  663.     printf("\f");
  664.     ad_line = FALSE;
  665.     }
  666. }
  667.  
  668. # function to add current line to parsed text
  669. function add_line() {
  670.     for ( i = 1 ; i <= NF ; i++ ) if ( length($i) ) addword($i);
  671. }
  672.  
  673. # output accumulated words
  674. function output_line(oline,leftm) {
  675.     if ( top ) print_top;
  676.     if ( leftm ) fprintf(outf,left_margin);
  677.     if ( list_left_mh ) {
  678.     left_margin = list_left_mh;
  679.     list_left_mh = FALSE;
  680.     }
  681.     gsub(req_be," ",oline);
  682.     fprint(outf,oline);
  683.     line_cnt += 1 + double_space;   # increment line counter
  684.     if ( line_cnt >= num_lines )    # check for end of page
  685.     end_page;   # function to print page numbers - note absence
  686.             # of '()' to invoke
  687.     else if ( double_space ) fprint(outf,"");
  688. }
  689.  
  690. # accumlate words for line
  691. function addword(w) {
  692.     local lw = length(w);   # length of added word
  693.  
  694.     if ( cnt + size + lw > width ) printline(yes); # check new line length
  695.     line[++cnt] = w;    # add word to line array
  696.     size += lw;
  697. }
  698.  
  699. # print accumulated words in justified line
  700. function printline(f) {
  701.     local i, nb, nsp, holes;
  702.     local tline;
  703.     local adj = adjust + cnt;
  704.     local fmt = format;
  705.  
  706.     if ( cnt ) {
  707.     if ( para_start && para_indent ) line[1] = copies(" ",para_indent) line[1];
  708.     if ( strlwr(f) == no && size  < (width - adj) ) fmt = fmt < right_justify ? left_justify : fmt;
  709.     tline = form_line(line,cnt,width,fmt);
  710.     output_line(tline,TRUE);
  711.     if ( set_width ) {
  712.         width = set_width;
  713.         set_width = FALSE;
  714.     }
  715.     size = cnt = 0;
  716.     deletea line;
  717.     }
  718.     if ( para_start ) para_start = FALSE;
  719. }
  720.  
  721. # eject current page - print blank lines to bottom,
  722. # print page number and form feed
  723. function page_eject() {
  724.     printline(no);
  725.     while ( ++line_cnt <= num_lines ) fprint(outf,"");
  726.     end_page;
  727. }
  728.  
  729. # end of page clean up - print page number & eject
  730. function end_page() {
  731.     local pnl = replace(ftr_line);    # replace defined variables in footer
  732.     local r_num = set_pnum(page_num);
  733.     local left_margin = copies(" ",d_left_m);
  734.     local ftrs = ftr_skip;
  735.  
  736.     if ( footer ) {
  737.     while ( ftrs-- ) fprint(outf,"");
  738.     sub(/#/,r_num,pnl);    # substitute for page number
  739.       # print left margin and footer text split across default page width
  740.     fprint(outf,left_margin split_text(pnl,d_width));
  741.       # use following line if want text centered instead of split
  742. #     fprint(outf,left_margin center(pnl,width));
  743.     }
  744.     fprintf(outf,"\f");
  745.     line_cnt = 0;
  746.     top = TRUE;
  747.     if ( wait_on_page ) {   # if waiting at end of page - display message, wait
  748.     fprintf(stderr,"Insert New Page, Press Carriage Return\n");
  749.     fgetline(stdin,pnl);
  750.     }
  751.     fprintf(stderr,"Finished Page: %s (%u)\n",set_pnum(page_num),ttl_page_cnt);
  752.     page_num++;
  753.     ttl_page_cnt++;
  754. }
  755.  
  756. # print top margin and header if any
  757. function print_top() {
  758.     local hdr_lne = replace(hdr_line);    # replace defined variables in header
  759.     local r_num = set_pnum(page_num);
  760.     local left_margin = copies(" ",d_left_m);
  761.     local hdrs = hdr_skip;
  762.  
  763.     top = FALSE;
  764.     # print top margin
  765.     while ( ++line_cnt <= top_margin ) fprint(outf,"");
  766.     if ( header ) {
  767.     sub(/#/, r_num , hdr_lne );  # substitute for page number
  768.       # print left margin and header text split across default page width
  769.     fprint(outf,left_margin split_text(hdr_lne,d_width));
  770.       # use following line if want text centered instead of split
  771. #     fprint(outf,left_margin center(hdr_lne,width));
  772.     while ( hdrs-- ) { fprint(outf,""); line_cnt++; }
  773.     }
  774. }
  775.  
  776. # function to convert decimal number to roman numeral
  777. # used if roman numeral page numbering desired - see roman_num variable
  778. function roman_numeral(num) {
  779.       # pwr_ten ==> maximum integer representable as roman numeral
  780.     local i, pwr_ten = 10000;
  781.     local frst_sym = '?', secd_sym = 'm', thrd_sym = '?';
  782.     local num_str = "";
  783.     local three_sym;
  784.  
  785.     num = int(num);
  786.     if ( num <= pwr_ten )
  787.     do {
  788.         pwr_ten /= 10;
  789.         i = num / pwr_ten;
  790.         num -= i * pwr_ten;
  791.         three_sym = "";
  792.         switch ( i ) {
  793.         case 3:
  794.             num_str ∩= secd_sym;
  795.         case 2:
  796.             num_str ∩= secd_sym;
  797.         case 1:
  798.             num_str ∩= secd_sym;
  799.             break;
  800.         case 4:
  801.             num_str ∩= secd_sym;
  802.             num_str ∩= thrd_sym;
  803.             break;
  804.         case 5:
  805.             num_str ∩= thrd_sym;
  806.             break;
  807.         case 8:
  808.             three_sym ∩= secd_sym;
  809.         case 7:
  810.             three_sym ∩= secd_sym;
  811.         case 6:
  812.             three_sym ∩= secd_sym;
  813.             num_str ∩= thrd_sym three_sym;
  814.             break;
  815.         case 9:
  816.             num_str ∩= secd_sym;
  817.             num_str ∩= frst_sym;
  818.             break;
  819.         case 0:
  820.             break;
  821.         }
  822.         switch ( frst_sym = secd_sym ) {
  823.         case 'm':
  824.             secd_sym = 'c';
  825.             thrd_sym = 'd';
  826.             break;
  827.         case 'c':
  828.             secd_sym = 'x';
  829.             thrd_sym = 'l';
  830.             break;
  831.         case 'x':
  832.             secd_sym = 'i';
  833.             thrd_sym = 'v';
  834.             break;
  835.         }
  836.     } while ( pwr_ten && num );
  837.     return num_str;
  838. }
  839.  
  840. # convert decimal number to alphabetic number - useful if alphabetic page
  841. # numbering wanted
  842. function d_to_alpha(num) {
  843.     local num_str = "";
  844.     local k;
  845.     local alphabet = "abcdefghijklmnopqrstuvwxyz";
  846.  
  847.     num = int(num);
  848.     if ( num > 0 && num <= 308915776 ) { # test for maximum number convertable
  849.     while ( num > 26 ) {
  850.         k = num % 26;
  851.         num /= 26;
  852.         if ( !k ) {
  853.         k = 26;
  854.         num--;
  855.         }
  856.         num_str = substr(alphabet,k,1) num_str;
  857.     }
  858.     num_str = substr(alphabet,num,1) num_str;
  859.     }
  860.     return num_str;
  861. }
  862.  
  863. # function to split text '.sx' control word
  864. function split_text(text,line_width) {
  865.     local cnt;
  866.     local sx_line, s_line;
  867.     local delim = substr(text,1,1);
  868.  
  869.       # split line on delimitor
  870.     cnt = split(text,s_line,delim);
  871.       # check for three fields
  872.     if ( cnt == 3 ) {
  873.       # center center portion in 'line_width'
  874.     sx_line = center(s_line[2],line_width,stx_chr);
  875.       # overlay left portion
  876.     sx_line = overlay(sx_line,s_line[1],1);
  877.       # overlay right portion
  878.     sx_line = overlay(sx_line,s_line[3],line_width - length(s_line[3]) + 1);
  879.       } else {
  880.     fprintf(stderr,"'.sx' Command Needs 3 Fields.\nLine: %d\nFile: %s\n",FNR,FILENAME);
  881.     exit 1;
  882.     }
  883.     return sx_line;
  884. }
  885.  
  886. # function to format input lines into table columns
  887. function format_table(iline) {
  888.     local oline, ocnt;
  889.     local cline, ccnt;
  890.     local pline = copies(" ",width);
  891.     local i, j;
  892.  
  893.     ocnt = split(iline,oline,t_sep);    # split input line into columns
  894.     for ( i in t_col ) {
  895.     if ( i > ocnt ) break;
  896.     ccnt = split(oline[i],cline,/[\s\t]+/); # split column into words
  897.     pline = overlay(pline,form_line(cline,ccnt,width,left_justify),t_col[i]);
  898.     }
  899.     output_line(pline,TRUE);
  900. }
  901.  
  902. # function to output list item
  903. function list_item() {
  904.     local lmrgn = length(left_margin);
  905.     local lsth;
  906.     local bchr, bhdr, bnum;
  907.     local blnk_sep = copies(req_bs,2);
  908.  
  909.     list_left_mh = left_margin;
  910.     left_margin = copies(" ",lmrgn - 4);
  911.     if ( list_list[list_level] > 0 ) { # ordered list
  912.     lsth = lstb[olist % olstn] blnk_sep;
  913.     # use numeric for odd levels, alpha for even levels
  914.     if ( olist & 1 ) $1 = list_list[list_level]++ lsth $1;
  915.       else $1 = d_to_alpha(list_list[list_level]++) lsth $1;
  916.       } else if ( !list_list[list_level] ) { # un-ordered list
  917.     if ( ulist & 1 ) bchr = '■'; else bchr = '-';
  918.     for ( bnum = int((ulist + 1) / 2) ; bnum ; bnum-- ) bhdr = bchr bhdr;
  919.     $1 = bhdr blnk_sep $1;
  920.     }
  921.     set_width = width;
  922.     width += 4;
  923.     add_line;
  924. }
  925.  
  926. # function to convert year/month/day into julian day number
  927. function jdn(year,month,day) {
  928.     local yr;
  929.     local pfac = 0.6;
  930.     local ljdn;
  931.  
  932.     yr = year + (month - 3.0) / 12.0;
  933.     ljdn = int(367.0 * yr + pfac) - (2 * int(yr)) + int(yr/4.0)
  934.        + int(day) + 1721117;
  935.     if ( !greg_jul ) ljdn += -int(yr/100.0) + int(yr/400.0) + 2;
  936.     return ljdn;
  937. }
  938.  
  939. # form array of words into output line
  940. # line ==> array of words
  941. # cnt ==>  number of elements in line
  942. # width ==> character width of line to form
  943. # format == 1 justify on width
  944. # format == right_justify || left justify
  945. function form_line(line,cnt,width,format) {
  946.     local i, oline;
  947.  
  948.     if ( format > TRUE ) {
  949.     for ( i = 2 , oline = line[1] ; i <= cnt ; i++ ) oline ∩= " " line[i];
  950.     if ( format == right_justify )
  951.       oline = copies(" ",width - length(oline)) ∩ oline;
  952.     } else oline = justify(line,cnt,width);
  953.     return oline;
  954. }
  955.  
  956. function print_toc() {
  957.     local tline;
  958.     local tcnt;
  959.     local stx_chr_h = stx_chr;
  960.     local hld_hdr = hdr_line;
  961.     local hld_ftr = ftr_line;
  962.     local prefix_hld = page_num_prefix;
  963.  
  964.     if ( !top ) page_eject;
  965.     hdr_line = toc_hdr_line;
  966.     ftr_line = toc_ftr_line;
  967.     page_num_prefix = "";
  968.     alpha_num = FALSE;
  969.     roman_num = TRUE;
  970.     page_num = toc_pnum;
  971.     output_line("Table of Contents",TRUE);
  972.     output_line("",TRUE);
  973.     for ( tcnt = 0 ; tcnt < tc_cnt ; tcnt++ ) {
  974.     if ( toc_lines[tcnt]["skip"] ) output_line("",TRUE);
  975.     tline = '\x0ff' ∩ toc_lines[tcnt]["line"] ∩ " " ∩ "\x0ff\x0ff" ∩ " " ∩ toc_lines[tcnt]["pgn"] ∩ '\x0ff';
  976.     stx_chr = '.';
  977.     tline = split_text(tline,d_width);
  978.     stx_chr = stx_chr_h;
  979.     output_line(tline,TRUE);
  980.     }
  981.     if ( !top ) page_eject;
  982.     hdr_line = hld_hdr;
  983.     ftr_line = hld_ftr;
  984.     page_num_prefix = prefix_hld;
  985. }
  986.  
  987. # return page number aaccording to style set
  988. function set_pnum(pnum) {
  989.     local r_num = roman_num ? roman_numeral(pnum) : alpha_num ? d_to_alpha(pnum) : pnum;
  990.  
  991.     return replace(page_num_prefix) ∩ r_num;
  992. }
  993.