home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 19 Printer / 19-Printer.zip / spr10.zip / spr.cmd < prev    next >
OS/2 REXX Batch file  |  1994-03-10  |  26KB  |  736 lines

  1. /* -------
  2.  * spr.cmd (C) SuperOscar Softwares, Tommi Nieminen 1994.
  3.  * -------
  4.  * Simple PRinter.
  5.  *
  6.  * Usage:
  7.  *     spr FILE ... [ SWITCHES ]
  8.  *
  9.  * 10-Mar-1994  v1.0 ready.
  10.  */
  11.  
  12. Parse Arg files '/'switches
  13.  
  14.   /* Program name, version, and copyright information */
  15. global.PRGNAME = 'SOS spr'
  16. global.VERSION = 'v1.0'
  17. global.COPYRIGHT = '(C) SuperOscar Softwares, Tommi Nieminen 1994'
  18.  
  19. Signal On Halt Name Abort
  20.  
  21.   /* Constants */
  22. TRUE = 1
  23. FALSE = 0
  24.  
  25.   /* Program status */
  26. global.ERRORLEVEL = 0
  27.  
  28.   /* Initialise option table */
  29. opttbl.0 = 0
  30.  
  31.   /* Valid options */
  32. optdes = 'a# starting-from# b# begin# c# pitch# e# end# f* footer*',
  33.          'fe* even-footer* fo* F# footers# odd-footer* h* header*',
  34.          'he* even-header* ho* odd-header* H# headers# k+ keep-long+',
  35.          'l# left# n# page-length# o* output* p* printer* p* printer*',
  36.          'q# quality* r# right# s# spacing# v version ? help'
  37.  
  38.   /* Parse switches in the environment variable first so that the
  39.    * values there can be overridden from the command line
  40.    */
  41. Call GetOpt Value('SPRSWITCHES',, 'OS2ENVIRONMENT'), optdes
  42.  
  43.   /* Parse command line switches (note that we have to append the `/'
  44.    * to the string since the `Parse Arg' instruction has removed it)
  45.    */
  46. If switches \= '' Then Call GetOpt '/'switches, optdes
  47.  
  48.   /* Defaults for local variables */
  49. firstpg = 1             /* Print from page 1 */
  50. init = ''               /* Default initialisation sequence */
  51. keeplong = FALSE        /* Keep lines that extend beyond right margin? */
  52. lastpg = 65536          /* Print all pages */
  53. leftmrg = 8             /* Default left margin */
  54. output = 'CON'          /* Output channel ('CON' is redirectable) */
  55. pagelen = 55            /* Page length in lines */
  56. pitch = 10              /* Character pitch in cpi's */
  57. printer = 'DUMB'        /* Printer */
  58. rightmrg = 72           /* Default right margin */
  59. quality = 0             /* Print quality (0..2, 0 is the lowest) */
  60. spacing = 6             /* Line spacing in lpi's */
  61. startingno = 1          /* Number of the first page */
  62. term = ''               /* Default termination sequence */
  63.  
  64.   /* Headers */
  65. nheaders = 1            /* Number of different headers (1 by default) */
  66. header = '$F||$#'       /* Default header */
  67. evenheader = '$#'       /* Default even header (not used unless /H=2) */
  68. oddheader = '||$#'      /* Default odd header (not used unless /H=2) */
  69.  
  70.   /* Footers */
  71. nfooters = 0            /* Number of different footers (0 by default) */
  72. footer = '|$#'          /* Default footer */
  73. evenfooter = ''
  74. oddfooter = ''
  75.  
  76.   /* Examine switch table */
  77. Do i = 1 To opttbl.0
  78.     key = opttbl._KEY.i; val = opttbl._VAL.i
  79.     Select
  80.         When key == 'a' | key == 'starting-from' Then startingno = val
  81.         When key == 'b' | key == 'begin' Then firstpg = val
  82.         When key == 'c' | key == 'pitch' Then pitch = val
  83.         When key == 'e' | key == 'end' Then lastpg = val
  84.         When key == 'f' | key == 'footer' Then footer = val
  85.         When key == 'fe' | key == 'even-footer' Then evenfooter = val
  86.         When key == 'fo' | key == 'odd-footer' Then oddfooter = val
  87.         When key == 'F' | key == 'footers' Then nfooters = val
  88.         When key == 'h' | key == 'header' Then header = val
  89.         When key == 'he' | key == 'even-header' Then evenheader = val
  90.         When key == 'ho' | key == 'odd-header' Then oddheader = val
  91.         When key == 'H' | key == 'headers' Then nheaders = val
  92.         When key == 'k' | key == 'keep-long' Then If val \= '-' Then keeplong = TRUE
  93.         When key == 'l' | key == 'left' Then leftmrg = val
  94.         When key == 'n' | key == 'page-length' Then pagelen = val
  95.         When key == 'o' | key == 'output' Then output = val
  96.         When key == 'p' | key == 'printer' Then printer = Translate(val)
  97.         When key == 'q' Then quality = Format(val)
  98.         When key == 'quality' Then
  99.             Select
  100.                 When val == 'draft' Then quality = 0
  101.                 When val == 'report' Then quality = 1
  102.                 When val == 'letter' Then quality = 2
  103.                 Otherwise Call Error 'invalid print quality: "'val'"'
  104.             End
  105.         When key == 'r' | key == 'right' Then rightmrg = val
  106.         When key == 's' | key == 'spacing' Then spacing = val
  107.         When key == 'v' | key == 'version' Then Call Version
  108.         When key == '?' | key == 'help' Then Call Help
  109.     End
  110. End
  111.  
  112.   /* Trap (some) invalid switch values */
  113. If firstpg > lastpg Then
  114.     Call Error 'the value of /b can''t be greater than the value of /e'
  115. If nfooters < 0 | nfooters > 2 Then
  116.     Call Error 'the only legal values for /F are 0 (no footers), 1, 2'
  117. If nheaders < 0 | nheaders > 2 Then
  118.     Call Error 'the only legal values for /H are 0 (no headers), 1, 2'
  119. If left < 0 | right < 0 | left >= right Then
  120.     Call Error 'invalid margins (left' left', right' right')'
  121. If pagelen <= 0 Then
  122.     Call Error 'invalid page length' pagelen
  123.  
  124. If files == '' Then
  125.     Call Error 'no files specified (use /? to get help)'
  126.  
  127.   /* Create left margin as a string of blanks */
  128. left_margin = Copies(' ', leftmrg)
  129.  
  130.   /* If any other printer than `DUMB', read printer definitions
  131.    * and send initialisation strings
  132.    */
  133. If printer \= 'DUMB' Then Do
  134.     Call ReadDefs(printer)
  135.  
  136.       /* Check printdef strings;  note that an undefined variable
  137.        * returns its name translated to upper case
  138.        */
  139.     If printdef.__INIT == 'PRINTDEF.__INIT' Then
  140.         printdef.__INIT = ''
  141.     If printdef.__PITCH.pitch == 'PRINTDEF.__PITCH.'pitch Then
  142.         Call Error 'printer doesn''t support' pitch 'cpi character pitch'
  143.     If printdef.__QUALITY.quality == 'PRINTDEF.__QUALITY.'quality Then
  144.         Call Error 'invalid print quality:' quality
  145.     If printdef.__SPACING.spacing == 'PRINTDEF.__SPACING.'spacing Then
  146.         Call Error 'printer doesn''t support' spacing 'lpi line spacing'
  147.     If printdef.__TERM == 'PRINTDEF.__TERM' Then
  148.         printdef.__TERM = ''
  149.  
  150.       /* Build the init sequence */
  151.     init = printdef.__INIT || printdef.__SPACING.spacing
  152.     init = init || printdef.__QUALITY.quality || printdef.__PITCH.pitch
  153. End
  154.  
  155.   /* Output all files */
  156. Do i = 1 To Words(files)
  157.       /* Get full path name for the file */
  158.     global.FILENAME = Stream(Word(files, i), 'C', 'Query Exists')
  159.  
  160.       /* Skip to the next file if this one doesn't exist */
  161.     If global.FILENAME == '' Then Do
  162.         Say global.PRGNAME' warning: file "'Word(files, i)'" not found'
  163.         Iterate
  164.     End
  165.     Else If global.FILENAME == '\DEV\CON' Then
  166.         global.FILENAME = ''
  167.  
  168.       /* If this is the first file, send init sequence.  This is
  169.        * placed here so that the file existence check is done first
  170.        */
  171.     If i == 1 & init \= '' Then Call SendLine output, init
  172.  
  173.     lineno = 0                  /* Line number counter */
  174.     global.PAGENO = startingno  /* Page number counter */
  175.     something_printed = FALSE   /* Nothing is printed yet */
  176.  
  177.       /* Read until EOF */
  178.     Do While Lines(global.FILENAME) > 0
  179.  
  180.           /* Read line, and update line counter */
  181.         line = LineIn(global.FILENAME)
  182.         lineno = lineno + 1
  183.  
  184.           /* Page breaks */
  185.         If lineno > pagelen Then Do
  186.             global.PAGENO = global.PAGENO + 1
  187.             lineno = 1
  188.         End
  189.  
  190.           /* Are we yet on printable area? */
  191.         If global.PAGENO >= firstpg Then Do
  192.               /* If this is the first line on a new page, create footers
  193.                * and headers
  194.                */
  195.             If lineno == 1 Then Do
  196.                   /* Dash-line to separate header or footer from text */
  197.                 dashline = Copies('-', rightmrg)
  198.  
  199.                   /* Print footers? (Never before the first header) */
  200.                 If nfooters > 0 & something_printed Then Do
  201.                       /* Change page counter value temporarily--footers
  202.                        * are printed only when the counter already marks
  203.                        * the new page
  204.                        */
  205.                     global.PAGENO = global.PAGENO - 1
  206.  
  207.                       /* Create appropriate footer */
  208.                     Select
  209.                         When nfooters == 1 Then
  210.                             ftr = CreateHeader(footer, rightmrg)
  211.                         When IsOdd(global.PAGENO) Then
  212.                             ftr = CreateHeader(oddfooter, rightmrg)
  213.                         Otherwise
  214.                             ftr = CreateHeader(evenfooter, rightmrg)
  215.                     End
  216.  
  217.                       /* Change page counter back to its new value */
  218.                     global.PAGENO = global.PAGENO + 1
  219.  
  220.                       /* Send blank line, dash-line, and footer */
  221.                     Call SendLine output, ''
  222.                     Call SendLine output, left_margin || dashline
  223.                     Call SendLine output, left_margin || ftr
  224.                 End
  225.  
  226.                   /* Leave the loop when lastpg reached */
  227.                 If global.PAGENO > lastpg Then Leave
  228.  
  229.                   /* Send form feed */
  230.                 If something_printed Then Call SendLine output, X2C(0C)
  231.  
  232.                   /* Print headers? */
  233.                 If nheaders > 0 Then Do
  234.                     Select
  235.                         When nheaders == 1 Then
  236.                             hdr = CreateHeader(header, rightmrg)
  237.                         When IsOdd(global.PAGENO) Then
  238.                             hdr = CreateHeader(oddheader, rightmrg)
  239.                         Otherwise
  240.                             hdr = CreateHeader(evenheader, rightmrg)
  241.                     End
  242.  
  243.                       /* Send header, dash-line, and blank line */
  244.                     Call SendLine output, left_margin || hdr
  245.                     Call SendLine output, left_margin || dashline
  246.                     Call SendLine output, ''
  247.                 End
  248.             End
  249.  
  250.               /* Cut line if allowed and necessary */
  251.             If \keeplong & (Length(line) > rightmrg) Then
  252.                 line = Left(line, rightmrg) || '»'
  253.  
  254.               /* Add left margin and print */
  255.             Call SendLine output, left_margin || line
  256.  
  257.               /* Now something is printed */
  258.             If \something_printed Then something_printed = TRUE
  259.         End
  260.     End
  261.  
  262.       /* The last footer is a special case: the page has to be filled
  263.        * with blank lines before the footer is printed
  264.        */
  265.     If nfooters > 0 & lineno > 1 Then Do
  266.           /* Fill page */
  267.         Do i = lineno To pagelen
  268.             Call SendLine output, ''
  269.         End
  270.  
  271.           /* Create appropriate footer */
  272.         Select
  273.             When nfooters == 1 Then
  274.                 ftr = CreateHeader(footer, rightmrg)
  275.             When IsOdd(global.PAGENO) Then
  276.                 ftr = CreateHeader(oddfooter, rightmrg)
  277.             Otherwise
  278.                 ftr = CreateHeader(evenfooter, rightmrg)
  279.         End
  280.  
  281.           /* Send blank line, dash-line, and footer */
  282.         Call SendLine output, ''
  283.         Call SendLine output, left_margin || dashline
  284.         Call SendLine output, left_margin || ftr
  285.     End
  286.  
  287.       /* Close the file (not 'CON', however) */
  288.     If global.FILENAME \= '' Then
  289.         Call Stream global.FILENAME, 'C', 'Close'
  290. End
  291.  
  292.   /* Send termination sequence */
  293. If term \= '' Then
  294.     Call SendLine output, printdef.__TERM
  295.  
  296. Abort:
  297.     Exit global.ERRORLEVEL
  298.  
  299.   /* Add a key-value pair to the switch tables, or change a value if
  300.    * the key already exists
  301.    */
  302. AddKeyToTable: Procedure Expose opttbl.
  303.     Parse Arg key, val
  304.  
  305.     Do i = 1 To opttbl.0
  306.         If key == opttbl._KEY.i Then Leave
  307.     End
  308.  
  309.     If i > opttbl.0 Then Do
  310.         count = opttbl.0 + 1
  311.         opttbl.0 = count
  312.         opttbl._KEY.count = key
  313.         opttbl._VAL.count = val
  314.     End
  315.     Else
  316.         opttbl._VAL.i = val
  317. Return
  318.  
  319.   /* Check that a switch matches its description */
  320. CheckSwitch: Procedure
  321.     Parse Arg key, val, refstr
  322.  
  323.     NULL = X2C(00)
  324.  
  325.     Select
  326.           /* Switch that shouldn't have a value */
  327.         When WordPos(key, refstr) > 0 & val == NULL Then
  328.             ok = 1
  329.           /* Switch with an optional trailing `+' or `-' */
  330.         When WordPos(key || '+', refstr) > 0 & Pos(val, '+-' || NULL) > 0 Then
  331.             ok = 1
  332.           /* Switch should have a numeric value */
  333.         When WordPos(key || '#', refstr) > 0 & DataType(val, "N") Then
  334.             ok = 1
  335.           /* Switch should have a string value */
  336.         When WordPos(key || '*', refstr) > 0 & val \= NULL Then
  337.             ok = 1
  338.         Otherwise
  339.             ok = 0
  340.     End
  341. Return ok
  342.  
  343.   /* Create a header line */
  344. CreateHeader: Procedure Expose global.
  345.     Parse Arg symstr, len
  346.  
  347.     part.0 = 1                  /* # of parts of symstr */
  348.     p1 = Pos('|', symstr)       /* First separator */
  349.     p2 = LastPos('|', symstr)   /* Last separator */
  350.  
  351.     hdr.1 = ''; hdr.2 = ''; hdr.3 = ''
  352.  
  353.       /* Separate parts */
  354.     Select
  355.         When p1 == 0 Then
  356.             part.1 = symstr
  357.  
  358.         When p1 == p2 Then Do
  359.             part.0 = 2
  360.             part.1 = Left(symstr, p1 - 1)
  361.             part.2 = SubStr(symstr, p1 + 1)
  362.         End
  363.  
  364.         Otherwise
  365.             part.0 = 3
  366.             part.1 = Left(symstr, p1 - 1)
  367.             part.2 = SubStr(symstr, p1 + 1, p2 - p1 - 1)
  368.             part.3 = SubStr(symstr, p2 + 1)
  369.     End
  370.  
  371.       /* Expand each part separately */
  372.     Do i = 1 To part.0
  373.         Do p1 = 1 To Length(part.i)
  374.             p2 = Pos('$', part.i, p1)
  375.  
  376.               /* Are there still `$' commands? */
  377.             If p2 \= 0 Then Do
  378.                   /* Copy everything between the current point and the
  379.                    * `$' to the header
  380.                    */
  381.                 hdr.i = hdr.i || SubStr(part.i, p1, p2 - p1)
  382.  
  383.                   /* Expand `$...' command to a string */
  384.                 hdr.i = hdr.i || ExpandCmd(SubStr(part.i, p2 + 1, 1))
  385.  
  386.                   /* Move pointer */
  387.                 p1 = p2 + 1
  388.             End
  389.             Else Do
  390.                 hdr.i = hdr.i || SubStr(part.i, p1)
  391.                 Leave
  392.             End
  393.         End
  394.     End
  395.  
  396.       /* Now combine the parts to a single string */
  397.     header = Center(hdr.2, len)
  398.     header = Overlay(hdr.1, header, 1)
  399.     header = Overlay(hdr.3, header, len - Length(hdr.3) + 1)
  400. Return header
  401.  
  402.   /* Expand a `$' command to a string */
  403. ExpandCmd: Procedure Expose global.
  404.     Parse Arg cmd
  405.  
  406.     If RxFuncQuery('SysOS2Ver') Then
  407.         Call RxFuncAdd 'SysOS2Ver', 'RexxUtil', 'SysOS2Ver'
  408.  
  409.     Select
  410.         When cmd == '#' Then
  411.             out = global.PAGENO
  412.         When cmd == '$' Then
  413.             out = '$'
  414.         When cmd == 'a' Then
  415.             out = Right(Time('C'), 2)
  416.         When cmd == 'd' Then
  417.             out = SubStr(Date('S'), 7, 2)
  418.         When cmd == 'f' Then
  419.             out = FileSpec('Name', global.FILENAME)
  420.         When cmd == 'F' Then
  421.             out = global.FILENAME
  422.         When cmd == 'h' Then
  423.             out = Left(Time(), 2)
  424.         When cmd == 'H' Then
  425.             out = Left(Time('C'), Pos(':', Time('C')) - 1)
  426.         When cmd == 'm' Then
  427.             out = SubStr(Date('S'), 5, 2)
  428.         When cmd == 'M' Then
  429.             out = SubStr(Time(), 4, 2)
  430.         When cmd == 'o' Then
  431.             out = Date('M')
  432.         When cmd == 'O' Then
  433.             out = Left(Date('M'), 3)
  434.         When cmd == 'y' Then
  435.             out = Left(Date('S'), 4)
  436.         When cmd == 'Y' Then
  437.             out = SubStr(Date('S'), 3, 2)
  438.         When cmd == 'v' Then
  439.             out = global.VERSION
  440.         When cmd == 'V' Then
  441.             out = SysOS2Ver()
  442.         When cmd == 'w' Then
  443.             out = Date('W')
  444.         When cmd == 'W' Then
  445.             out = Left(Date('W'), 3)
  446.         When cmd == 'z' | cmd == 'Z' Then Do
  447.             size = Stream(global.FILENAME, 'C', 'Query Size')
  448.             If cmd == 'Z' Then size = size % 1024
  449.             out = Format(size)
  450.         End
  451.         Otherwise
  452.             Call Error 'invalid command "$'cmd'"'
  453.     End
  454. Return out
  455.  
  456.   /* Expand symbolic string to binary format */
  457. ExpandSym: Procedure Expose global.
  458.     Parse Arg symstr, lineno
  459.  
  460.     outstr = ''
  461.     Do i = 1 To Length(symstr)
  462.         ch = SubStr(symstr, i, 1)
  463.         If ch == '\' Then Do
  464.               /* Move pointer to the next character */
  465.             i = i + 1
  466.             ch = SubStr(symstr, i, 1)
  467.             Select
  468.                 When ch == '0' Then outstr = outstr || X2C(00)
  469.                 When ch == 'e' Then outstr = outstr || X2C(1B)
  470.                 When ch == 'f' Then outstr = outstr || X2C(0C)
  471.                 When ch == 'n' Then outstr = outstr || X2C(0A)
  472.                 When ch == 'r' Then outstr = outstr || X2C(0D)
  473.                 When ch == 't' Then outstr = outstr || X2C(09)
  474.                 When ch == 'x' Then Do
  475.                     hex = SubStr(symstr, i + 1, 2)
  476.                     If \DataType(hex, 'X') Then Do
  477.                         global.ERRORLEVEL = 1
  478.                         Return ''
  479.                     End
  480.                     outstr = outstr || X2C(hex)
  481.                       /* Move past the hex digit */
  482.                     i = i + 2
  483.                 End
  484.                 Otherwise
  485.                     global.ERRORLEVEL = 1
  486.                     Return ''
  487.             End
  488.         End
  489.         Else
  490.             outstr = outstr || ch
  491.     End
  492. Return outstr
  493.  
  494.   /* GetOpt(): Parse switch string into key-value pairs. */
  495. GetOpt: Procedure Expose global. opttbl.
  496.     Parse Arg optstr, optdes
  497.  
  498.     NULL = X2C(00)
  499.  
  500.     Do i = 1 To Length(optstr)
  501.         key = ''
  502.         val = NULL        /* Something not likely to occur */
  503.  
  504.         ch = SubStr(optstr, i, 1)
  505.         Select
  506.               /* Skip blanks */
  507.             When ch == ' ' Then
  508.                 Iterate
  509.  
  510.               /* A slash `/' begins a switch */
  511.             When ch == '/' Then Do
  512.                 endkey = 0
  513.  
  514.                   /* Separate this switch (key + possible value) from
  515.                    * the others, if any
  516.                    */
  517.                 Do j = i + 1 While endkey == 0
  518.                     nextslash = Pos('/', optstr, j)
  519.                     nextquote = Pos('"', optstr, j)
  520.  
  521.                       /* If the next slash is inside quotes, find the
  522.                        * closing quote and continue the loop
  523.                        */
  524.                     If nextquote \= 0 & nextquote < nextslash Then Do
  525.                         j = Pos('"', optstr, nextquote + 1)
  526.                           /* If an odd number of quotes, raise error */
  527.                         If j == 0 Then Call Error 'missing quotes'
  528.                     End
  529.                     Else If nextslash \= 0 Then
  530.                         endkey = nextslash
  531.                     Else
  532.                         endkey = Length(optstr) + 1
  533.                 End
  534.  
  535.                 key = Strip(SubStr(optstr, i + 1, endkey - i - 1))
  536.  
  537.                   /* Check whether there is a value too for this switch */
  538.                 Do Until begval \= 0
  539.                     nextquote = Pos('"', key)
  540.                     nextequ = Pos('=', key)
  541.  
  542.                       /* If the next equation sign is inside quotes,
  543.                        * find the closing quote and continue the loop
  544.                        */
  545.                     If nextquote \= 0 & nextquote < nextequ Then Do
  546.                         j = Pos('"', key, nextquote + 1)
  547.                           /* If an odd number of quotes, raise error */
  548.                         If j == 0 Then Call Error 'missing quotes'
  549.                     End
  550.                       /* If no equation signs outside of quotes were
  551.                        * found, there is no value
  552.                        */
  553.                     Else If nextequ == 0 Then
  554.                         begval = -1
  555.                     Else
  556.                         begval = nextequ + 1
  557.                 End
  558.  
  559.                   /* Separate key from numeric or string value */
  560.                 If begval \= -1 Then Do
  561.                     val = Strip(SubStr(key, begval))
  562.                     key = Left(key, begval - 2)
  563.  
  564.                       /* Strip quotes */
  565.                     val = Strip(val,, '"')
  566.                 End
  567.  
  568.                   /* Separate key from trailing plus/minus */
  569.                 If Pos(Right(key, 1), '+-') > 0 Then Do
  570.                     val = Right(key, 1)
  571.                     key = Left(key, Length(key) - 1)
  572.                 End
  573.  
  574.                   /* Check that the switch matches its description */
  575.                 If \CheckSwitch(key, val, optdes) Then
  576.                     Call Error 'invalid switch or value: "/'key'"'
  577.  
  578.                   /* Add the key (and its value, if any) to the
  579.                    * option table;  the `__' prefix tries to make
  580.                    * sure no variable of the same name is ever used
  581.                    */
  582.                 Call AddKeyToTable key, val
  583.  
  584.                   /* Move the pointer just before the next switch (`Do'
  585.                    * increases i by one at the end of the loop!)
  586.                    */
  587.                 i = endkey - 1
  588.             End
  589.  
  590.               /* Invalid character encountered */
  591.             Otherwise
  592.                 Call Error 'invalid command line syntax'
  593.         End
  594.     End
  595. Return 0
  596.  
  597.   /* Check whether or not a given switch is defined */
  598. IsDefined: Procedure
  599.     Arg var
  600.  
  601.     ok = 0
  602.       /* An undefined variable returns its name */
  603.     If Value(var) == Translate(var) Then ok = 1
  604. Return ok
  605.  
  606.   /* Odd or even number? */
  607. IsOdd: Procedure
  608.     Arg num
  609.  
  610.     ok = 0
  611.     If num / 2 \= num % 2 Then ok = 1
  612. Return ok
  613.  
  614.   /* Read printer definition file */
  615. ReadDefs: Procedure Expose global. printdef.
  616.     Parse Arg printer
  617.  
  618.       /* Error `infix' for invalid definition files */
  619.     errinfix = 'on definition file line'
  620.  
  621.       /* Load SysSearchPath() function if not already loaded */
  622.     If RxFuncQuery('SysSearchPath') Then
  623.         Call RxFuncAdd 'SysSearchPath', 'RexxUtil', 'SysSearchPath'
  624.  
  625.       /* Compose definition file name */
  626.     file = printer || '.def'
  627.  
  628.       /* Search the file using SPRDEFPATH environment variable */
  629.     fname = SysSearchPath('SPRDEFPATH', file)
  630.     If fname == '' Then
  631.         Call Error 'can''t find definition file for' printer 'printer'
  632.  
  633.       /* Read file line by line */
  634.     Do lineno = 1 While Lines(fname) > 0
  635.         line = Strip(LineIn(fname))
  636.  
  637.           /* Ignore blank and comment lines */
  638.         If line == '' | Left(line, 1) == ';' Then Iterate
  639.  
  640.           /* Separate key and its value */
  641.         key = Translate(Word(line, 1))
  642.         val = Strip(Word(line, 2),, '"')
  643.  
  644.           /* Does the key have attributes? */
  645.         If Pos('(', key) > 0 Then Do
  646.             beg = Pos('(', key)
  647.             end = Pos(')', key)
  648.  
  649.             If beg == 0 | beg > end Then
  650.                 Call Error 'mismatching parentheses' errinfix lineno
  651.  
  652.             attr = SubStr(key, beg + 1, end - beg - 1)
  653.             key = Left(key, beg - 1)
  654.         End
  655.  
  656.           /* Add a `__' prefix to the key name to prevent the name
  657.            * colliding with an existing variable name
  658.            */
  659.         keyname = '__' || key
  660.  
  661.         Select
  662.               /* These keys don't have attributes */
  663.             When WordPos(key, 'INIT TERM') > 0 Then Do
  664.                 printdef.keyname = ExpandSym(val)
  665.                 if global.ERRORLEVEL > 0 Then
  666.                     Call Error 'invalid backslash quote' errinfix lineno
  667.             End
  668.  
  669.               /* These keys do have attributes */
  670.             When WordPos(key, 'PITCH QUALITY SPACING') > 0 Then Do
  671.                 If attr == '' Then
  672.                     Call Error '"'key'" requires an attribute' errinfix lineno
  673.                 printdef.keyname.attr = ExpandSym(val)
  674.                 If global.ERRORLEVEL > 0 Then
  675.                     Call Error 'invalid backslash quote' errinfix lineno
  676.             End
  677.  
  678.               /* Unknown key */
  679.             Otherwise
  680.                 Call Virhe 'Unknown key' errinfix lineno
  681.         End
  682.     End
  683.  
  684.       /* Close file */
  685.     Call Stream fname, 'C', 'Close'
  686. Return
  687.  
  688.   /* Send a line to a file or device */
  689. SendLine: Procedure Expose global.
  690.     Parse Arg output, str
  691.  
  692.     If Translate(output) \= 'CON' Then
  693.         Call LineOut output, str
  694.     Else
  695.         Say str
  696. Return
  697.  
  698.   /* Display an error message and quit */
  699. Error: Procedure Expose global.
  700.     Parse Arg msg
  701.  
  702.     Say global.PRGNAME':' msg
  703.     global.ERRORLEVEL = 1
  704. Signal Abort
  705.  
  706.   /* Display program version and quit */
  707. Version: Procedure Expose global.
  708.     Say global.PRGNAME global.VERSION global.COPYRIGHT
  709. Signal Abort
  710.  
  711.   /* Display help page and quit */
  712. Help: Procedure Expose global.
  713.     Say global.PRGNAME global.VERSION global.COPYRIGHT
  714.     Say
  715.     Say 'Simple PRinter.'
  716.     Say
  717.     Say 'Usage:'
  718.     Say '    spr FILE ... [ SWITCHES ]'
  719.     Say
  720.     Say 'You may use `con'' as the file name--this enables piping and re-'
  721.     Say 'directing output to spr.'
  722.     Say
  723.     Say 'Switches:'
  724.     Say '    /a=N    /starting-from=N        /b=N    /begin=N'
  725.     Say '    /c=N    /pitch=N                /e=N    /end=N'
  726.     Say '    /f=STR  /footer=STR             /fe=STR /even-footer=STR'
  727.     Say '    /fo=STR /odd-footer=STR         /F=N    /footers=N'
  728.     Say '    /h=STR  /header=STR             /he=STR /even-header=STR'
  729.     Say '    /ho=STR /odd-header=STR         /H=N    /headers=N'
  730.     Say '    /k[+|-] /keep-long[+|-]         /l=N    /left=N'
  731.     Say '    /n=N    /page-length=N          /o=STR  /output=STR'
  732.     Say '    /p=STR  /printer=STR            /q=N    /quality=STR'
  733.     Say '    /r=N    /right=N                /s=N    /spacing=N'
  734.     Say '    /v      /version                /?      /help'
  735. Signal Abort
  736.