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