home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 19 Printer
/
19-Printer.zip
/
spr10.zip
/
spr.cmd
< prev
next >
Wrap
OS/2 REXX Batch file
|
1994-03-10
|
26KB
|
736 lines
/* -------
* spr.cmd (C) SuperOscar Softwares, Tommi Nieminen 1994.
* -------
* Simple PRinter.
*
* Usage:
* spr FILE ... [ SWITCHES ]
*
* 10-Mar-1994 v1.0 ready.
*/
Parse Arg files '/'switches
/* Program name, version, and copyright information */
global.PRGNAME = 'SOS spr'
global.VERSION = 'v1.0'
global.COPYRIGHT = '(C) SuperOscar Softwares, Tommi Nieminen 1994'
Signal On Halt Name Abort
/* Constants */
TRUE = 1
FALSE = 0
/* Program status */
global.ERRORLEVEL = 0
/* Initialise option table */
opttbl.0 = 0
/* Valid options */
optdes = 'a# starting-from# b# begin# c# pitch# e# end# f* footer*',
'fe* even-footer* fo* F# footers# odd-footer* h* header*',
'he* even-header* ho* odd-header* H# headers# k+ keep-long+',
'l# left# n# page-length# o* output* p* printer* p* printer*',
'q# quality* r# right# s# spacing# v version ? help'
/* Parse switches in the environment variable first so that the
* values there can be overridden from the command line
*/
Call GetOpt Value('SPRSWITCHES',, 'OS2ENVIRONMENT'), optdes
/* Parse command line switches (note that we have to append the `/'
* to the string since the `Parse Arg' instruction has removed it)
*/
If switches \= '' Then Call GetOpt '/'switches, optdes
/* Defaults for local variables */
firstpg = 1 /* Print from page 1 */
init = '' /* Default initialisation sequence */
keeplong = FALSE /* Keep lines that extend beyond right margin? */
lastpg = 65536 /* Print all pages */
leftmrg = 8 /* Default left margin */
output = 'CON' /* Output channel ('CON' is redirectable) */
pagelen = 55 /* Page length in lines */
pitch = 10 /* Character pitch in cpi's */
printer = 'DUMB' /* Printer */
rightmrg = 72 /* Default right margin */
quality = 0 /* Print quality (0..2, 0 is the lowest) */
spacing = 6 /* Line spacing in lpi's */
startingno = 1 /* Number of the first page */
term = '' /* Default termination sequence */
/* Headers */
nheaders = 1 /* Number of different headers (1 by default) */
header = '$F||$#' /* Default header */
evenheader = '$#' /* Default even header (not used unless /H=2) */
oddheader = '||$#' /* Default odd header (not used unless /H=2) */
/* Footers */
nfooters = 0 /* Number of different footers (0 by default) */
footer = '|$#' /* Default footer */
evenfooter = ''
oddfooter = ''
/* Examine switch table */
Do i = 1 To opttbl.0
key = opttbl._KEY.i; val = opttbl._VAL.i
Select
When key == 'a' | key == 'starting-from' Then startingno = val
When key == 'b' | key == 'begin' Then firstpg = val
When key == 'c' | key == 'pitch' Then pitch = val
When key == 'e' | key == 'end' Then lastpg = val
When key == 'f' | key == 'footer' Then footer = val
When key == 'fe' | key == 'even-footer' Then evenfooter = val
When key == 'fo' | key == 'odd-footer' Then oddfooter = val
When key == 'F' | key == 'footers' Then nfooters = val
When key == 'h' | key == 'header' Then header = val
When key == 'he' | key == 'even-header' Then evenheader = val
When key == 'ho' | key == 'odd-header' Then oddheader = val
When key == 'H' | key == 'headers' Then nheaders = val
When key == 'k' | key == 'keep-long' Then If val \= '-' Then keeplong = TRUE
When key == 'l' | key == 'left' Then leftmrg = val
When key == 'n' | key == 'page-length' Then pagelen = val
When key == 'o' | key == 'output' Then output = val
When key == 'p' | key == 'printer' Then printer = Translate(val)
When key == 'q' Then quality = Format(val)
When key == 'quality' Then
Select
When val == 'draft' Then quality = 0
When val == 'report' Then quality = 1
When val == 'letter' Then quality = 2
Otherwise Call Error 'invalid print quality: "'val'"'
End
When key == 'r' | key == 'right' Then rightmrg = val
When key == 's' | key == 'spacing' Then spacing = val
When key == 'v' | key == 'version' Then Call Version
When key == '?' | key == 'help' Then Call Help
End
End
/* Trap (some) invalid switch values */
If firstpg > lastpg Then
Call Error 'the value of /b can''t be greater than the value of /e'
If nfooters < 0 | nfooters > 2 Then
Call Error 'the only legal values for /F are 0 (no footers), 1, 2'
If nheaders < 0 | nheaders > 2 Then
Call Error 'the only legal values for /H are 0 (no headers), 1, 2'
If left < 0 | right < 0 | left >= right Then
Call Error 'invalid margins (left' left', right' right')'
If pagelen <= 0 Then
Call Error 'invalid page length' pagelen
If files == '' Then
Call Error 'no files specified (use /? to get help)'
/* Create left margin as a string of blanks */
left_margin = Copies(' ', leftmrg)
/* If any other printer than `DUMB', read printer definitions
* and send initialisation strings
*/
If printer \= 'DUMB' Then Do
Call ReadDefs(printer)
/* Check printdef strings; note that an undefined variable
* returns its name translated to upper case
*/
If printdef.__INIT == 'PRINTDEF.__INIT' Then
printdef.__INIT = ''
If printdef.__PITCH.pitch == 'PRINTDEF.__PITCH.'pitch Then
Call Error 'printer doesn''t support' pitch 'cpi character pitch'
If printdef.__QUALITY.quality == 'PRINTDEF.__QUALITY.'quality Then
Call Error 'invalid print quality:' quality
If printdef.__SPACING.spacing == 'PRINTDEF.__SPACING.'spacing Then
Call Error 'printer doesn''t support' spacing 'lpi line spacing'
If printdef.__TERM == 'PRINTDEF.__TERM' Then
printdef.__TERM = ''
/* Build the init sequence */
init = printdef.__INIT || printdef.__SPACING.spacing
init = init || printdef.__QUALITY.quality || printdef.__PITCH.pitch
End
/* Output all files */
Do i = 1 To Words(files)
/* Get full path name for the file */
global.FILENAME = Stream(Word(files, i), 'C', 'Query Exists')
/* Skip to the next file if this one doesn't exist */
If global.FILENAME == '' Then Do
Say global.PRGNAME' warning: file "'Word(files, i)'" not found'
Iterate
End
Else If global.FILENAME == '\DEV\CON' Then
global.FILENAME = ''
/* If this is the first file, send init sequence. This is
* placed here so that the file existence check is done first
*/
If i == 1 & init \= '' Then Call SendLine output, init
lineno = 0 /* Line number counter */
global.PAGENO = startingno /* Page number counter */
something_printed = FALSE /* Nothing is printed yet */
/* Read until EOF */
Do While Lines(global.FILENAME) > 0
/* Read line, and update line counter */
line = LineIn(global.FILENAME)
lineno = lineno + 1
/* Page breaks */
If lineno > pagelen Then Do
global.PAGENO = global.PAGENO + 1
lineno = 1
End
/* Are we yet on printable area? */
If global.PAGENO >= firstpg Then Do
/* If this is the first line on a new page, create footers
* and headers
*/
If lineno == 1 Then Do
/* Dash-line to separate header or footer from text */
dashline = Copies('-', rightmrg)
/* Print footers? (Never before the first header) */
If nfooters > 0 & something_printed Then Do
/* Change page counter value temporarily--footers
* are printed only when the counter already marks
* the new page
*/
global.PAGENO = global.PAGENO - 1
/* Create appropriate footer */
Select
When nfooters == 1 Then
ftr = CreateHeader(footer, rightmrg)
When IsOdd(global.PAGENO) Then
ftr = CreateHeader(oddfooter, rightmrg)
Otherwise
ftr = CreateHeader(evenfooter, rightmrg)
End
/* Change page counter back to its new value */
global.PAGENO = global.PAGENO + 1
/* Send blank line, dash-line, and footer */
Call SendLine output, ''
Call SendLine output, left_margin || dashline
Call SendLine output, left_margin || ftr
End
/* Leave the loop when lastpg reached */
If global.PAGENO > lastpg Then Leave
/* Send form feed */
If something_printed Then Call SendLine output, X2C(0C)
/* Print headers? */
If nheaders > 0 Then Do
Select
When nheaders == 1 Then
hdr = CreateHeader(header, rightmrg)
When IsOdd(global.PAGENO) Then
hdr = CreateHeader(oddheader, rightmrg)
Otherwise
hdr = CreateHeader(evenheader, rightmrg)
End
/* Send header, dash-line, and blank line */
Call SendLine output, left_margin || hdr
Call SendLine output, left_margin || dashline
Call SendLine output, ''
End
End
/* Cut line if allowed and necessary */
If \keeplong & (Length(line) > rightmrg) Then
line = Left(line, rightmrg) || '»'
/* Add left margin and print */
Call SendLine output, left_margin || line
/* Now something is printed */
If \something_printed Then something_printed = TRUE
End
End
/* The last footer is a special case: the page has to be filled
* with blank lines before the footer is printed
*/
If nfooters > 0 & lineno > 1 Then Do
/* Fill page */
Do i = lineno To pagelen
Call SendLine output, ''
End
/* Create appropriate footer */
Select
When nfooters == 1 Then
ftr = CreateHeader(footer, rightmrg)
When IsOdd(global.PAGENO) Then
ftr = CreateHeader(oddfooter, rightmrg)
Otherwise
ftr = CreateHeader(evenfooter, rightmrg)
End
/* Send blank line, dash-line, and footer */
Call SendLine output, ''
Call SendLine output, left_margin || dashline
Call SendLine output, left_margin || ftr
End
/* Close the file (not 'CON', however) */
If global.FILENAME \= '' Then
Call Stream global.FILENAME, 'C', 'Close'
End
/* Send termination sequence */
If term \= '' Then
Call SendLine output, printdef.__TERM
Abort:
Exit global.ERRORLEVEL
/* Add a key-value pair to the switch tables, or change a value if
* the key already exists
*/
AddKeyToTable: Procedure Expose opttbl.
Parse Arg key, val
Do i = 1 To opttbl.0
If key == opttbl._KEY.i Then Leave
End
If i > opttbl.0 Then Do
count = opttbl.0 + 1
opttbl.0 = count
opttbl._KEY.count = key
opttbl._VAL.count = val
End
Else
opttbl._VAL.i = val
Return
/* Check that a switch matches its description */
CheckSwitch: Procedure
Parse Arg key, val, refstr
NULL = X2C(00)
Select
/* Switch that shouldn't have a value */
When WordPos(key, refstr) > 0 & val == NULL Then
ok = 1
/* Switch with an optional trailing `+' or `-' */
When WordPos(key || '+', refstr) > 0 & Pos(val, '+-' || NULL) > 0 Then
ok = 1
/* Switch should have a numeric value */
When WordPos(key || '#', refstr) > 0 & DataType(val, "N") Then
ok = 1
/* Switch should have a string value */
When WordPos(key || '*', refstr) > 0 & val \= NULL Then
ok = 1
Otherwise
ok = 0
End
Return ok
/* Create a header line */
CreateHeader: Procedure Expose global.
Parse Arg symstr, len
part.0 = 1 /* # of parts of symstr */
p1 = Pos('|', symstr) /* First separator */
p2 = LastPos('|', symstr) /* Last separator */
hdr.1 = ''; hdr.2 = ''; hdr.3 = ''
/* Separate parts */
Select
When p1 == 0 Then
part.1 = symstr
When p1 == p2 Then Do
part.0 = 2
part.1 = Left(symstr, p1 - 1)
part.2 = SubStr(symstr, p1 + 1)
End
Otherwise
part.0 = 3
part.1 = Left(symstr, p1 - 1)
part.2 = SubStr(symstr, p1 + 1, p2 - p1 - 1)
part.3 = SubStr(symstr, p2 + 1)
End
/* Expand each part separately */
Do i = 1 To part.0
Do p1 = 1 To Length(part.i)
p2 = Pos('$', part.i, p1)
/* Are there still `$' commands? */
If p2 \= 0 Then Do
/* Copy everything between the current point and the
* `$' to the header
*/
hdr.i = hdr.i || SubStr(part.i, p1, p2 - p1)
/* Expand `$...' command to a string */
hdr.i = hdr.i || ExpandCmd(SubStr(part.i, p2 + 1, 1))
/* Move pointer */
p1 = p2 + 1
End
Else Do
hdr.i = hdr.i || SubStr(part.i, p1)
Leave
End
End
End
/* Now combine the parts to a single string */
header = Center(hdr.2, len)
header = Overlay(hdr.1, header, 1)
header = Overlay(hdr.3, header, len - Length(hdr.3) + 1)
Return header
/* Expand a `$' command to a string */
ExpandCmd: Procedure Expose global.
Parse Arg cmd
If RxFuncQuery('SysOS2Ver') Then
Call RxFuncAdd 'SysOS2Ver', 'RexxUtil', 'SysOS2Ver'
Select
When cmd == '#' Then
out = global.PAGENO
When cmd == '$' Then
out = '$'
When cmd == 'a' Then
out = Right(Time('C'), 2)
When cmd == 'd' Then
out = SubStr(Date('S'), 7, 2)
When cmd == 'f' Then
out = FileSpec('Name', global.FILENAME)
When cmd == 'F' Then
out = global.FILENAME
When cmd == 'h' Then
out = Left(Time(), 2)
When cmd == 'H' Then
out = Left(Time('C'), Pos(':', Time('C')) - 1)
When cmd == 'm' Then
out = SubStr(Date('S'), 5, 2)
When cmd == 'M' Then
out = SubStr(Time(), 4, 2)
When cmd == 'o' Then
out = Date('M')
When cmd == 'O' Then
out = Left(Date('M'), 3)
When cmd == 'y' Then
out = Left(Date('S'), 4)
When cmd == 'Y' Then
out = SubStr(Date('S'), 3, 2)
When cmd == 'v' Then
out = global.VERSION
When cmd == 'V' Then
out = SysOS2Ver()
When cmd == 'w' Then
out = Date('W')
When cmd == 'W' Then
out = Left(Date('W'), 3)
When cmd == 'z' | cmd == 'Z' Then Do
size = Stream(global.FILENAME, 'C', 'Query Size')
If cmd == 'Z' Then size = size % 1024
out = Format(size)
End
Otherwise
Call Error 'invalid command "$'cmd'"'
End
Return out
/* Expand symbolic string to binary format */
ExpandSym: Procedure Expose global.
Parse Arg symstr, lineno
outstr = ''
Do i = 1 To Length(symstr)
ch = SubStr(symstr, i, 1)
If ch == '\' Then Do
/* Move pointer to the next character */
i = i + 1
ch = SubStr(symstr, i, 1)
Select
When ch == '0' Then outstr = outstr || X2C(00)
When ch == 'e' Then outstr = outstr || X2C(1B)
When ch == 'f' Then outstr = outstr || X2C(0C)
When ch == 'n' Then outstr = outstr || X2C(0A)
When ch == 'r' Then outstr = outstr || X2C(0D)
When ch == 't' Then outstr = outstr || X2C(09)
When ch == 'x' Then Do
hex = SubStr(symstr, i + 1, 2)
If \DataType(hex, 'X') Then Do
global.ERRORLEVEL = 1
Return ''
End
outstr = outstr || X2C(hex)
/* Move past the hex digit */
i = i + 2
End
Otherwise
global.ERRORLEVEL = 1
Return ''
End
End
Else
outstr = outstr || ch
End
Return outstr
/* GetOpt(): Parse switch string into key-value pairs. */
GetOpt: Procedure Expose global. opttbl.
Parse Arg optstr, optdes
NULL = X2C(00)
Do i = 1 To Length(optstr)
key = ''
val = NULL /* Something not likely to occur */
ch = SubStr(optstr, i, 1)
Select
/* Skip blanks */
When ch == ' ' Then
Iterate
/* A slash `/' begins a switch */
When ch == '/' Then Do
endkey = 0
/* Separate this switch (key + possible value) from
* the others, if any
*/
Do j = i + 1 While endkey == 0
nextslash = Pos('/', optstr, j)
nextquote = Pos('"', optstr, j)
/* If the next slash is inside quotes, find the
* closing quote and continue the loop
*/
If nextquote \= 0 & nextquote < nextslash Then Do
j = Pos('"', optstr, nextquote + 1)
/* If an odd number of quotes, raise error */
If j == 0 Then Call Error 'missing quotes'
End
Else If nextslash \= 0 Then
endkey = nextslash
Else
endkey = Length(optstr) + 1
End
key = Strip(SubStr(optstr, i + 1, endkey - i - 1))
/* Check whether there is a value too for this switch */
Do Until begval \= 0
nextquote = Pos('"', key)
nextequ = Pos('=', key)
/* If the next equation sign is inside quotes,
* find the closing quote and continue the loop
*/
If nextquote \= 0 & nextquote < nextequ Then Do
j = Pos('"', key, nextquote + 1)
/* If an odd number of quotes, raise error */
If j == 0 Then Call Error 'missing quotes'
End
/* If no equation signs outside of quotes were
* found, there is no value
*/
Else If nextequ == 0 Then
begval = -1
Else
begval = nextequ + 1
End
/* Separate key from numeric or string value */
If begval \= -1 Then Do
val = Strip(SubStr(key, begval))
key = Left(key, begval - 2)
/* Strip quotes */
val = Strip(val,, '"')
End
/* Separate key from trailing plus/minus */
If Pos(Right(key, 1), '+-') > 0 Then Do
val = Right(key, 1)
key = Left(key, Length(key) - 1)
End
/* Check that the switch matches its description */
If \CheckSwitch(key, val, optdes) Then
Call Error 'invalid switch or value: "/'key'"'
/* Add the key (and its value, if any) to the
* option table; the `__' prefix tries to make
* sure no variable of the same name is ever used
*/
Call AddKeyToTable key, val
/* Move the pointer just before the next switch (`Do'
* increases i by one at the end of the loop!)
*/
i = endkey - 1
End
/* Invalid character encountered */
Otherwise
Call Error 'invalid command line syntax'
End
End
Return 0
/* Check whether or not a given switch is defined */
IsDefined: Procedure
Arg var
ok = 0
/* An undefined variable returns its name */
If Value(var) == Translate(var) Then ok = 1
Return ok
/* Odd or even number? */
IsOdd: Procedure
Arg num
ok = 0
If num / 2 \= num % 2 Then ok = 1
Return ok
/* Read printer definition file */
ReadDefs: Procedure Expose global. printdef.
Parse Arg printer
/* Error `infix' for invalid definition files */
errinfix = 'on definition file line'
/* Load SysSearchPath() function if not already loaded */
If RxFuncQuery('SysSearchPath') Then
Call RxFuncAdd 'SysSearchPath', 'RexxUtil', 'SysSearchPath'
/* Compose definition file name */
file = printer || '.def'
/* Search the file using SPRDEFPATH environment variable */
fname = SysSearchPath('SPRDEFPATH', file)
If fname == '' Then
Call Error 'can''t find definition file for' printer 'printer'
/* Read file line by line */
Do lineno = 1 While Lines(fname) > 0
line = Strip(LineIn(fname))
/* Ignore blank and comment lines */
If line == '' | Left(line, 1) == ';' Then Iterate
/* Separate key and its value */
key = Translate(Word(line, 1))
val = Strip(Word(line, 2),, '"')
/* Does the key have attributes? */
If Pos('(', key) > 0 Then Do
beg = Pos('(', key)
end = Pos(')', key)
If beg == 0 | beg > end Then
Call Error 'mismatching parentheses' errinfix lineno
attr = SubStr(key, beg + 1, end - beg - 1)
key = Left(key, beg - 1)
End
/* Add a `__' prefix to the key name to prevent the name
* colliding with an existing variable name
*/
keyname = '__' || key
Select
/* These keys don't have attributes */
When WordPos(key, 'INIT TERM') > 0 Then Do
printdef.keyname = ExpandSym(val)
if global.ERRORLEVEL > 0 Then
Call Error 'invalid backslash quote' errinfix lineno
End
/* These keys do have attributes */
When WordPos(key, 'PITCH QUALITY SPACING') > 0 Then Do
If attr == '' Then
Call Error '"'key'" requires an attribute' errinfix lineno
printdef.keyname.attr = ExpandSym(val)
If global.ERRORLEVEL > 0 Then
Call Error 'invalid backslash quote' errinfix lineno
End
/* Unknown key */
Otherwise
Call Virhe 'Unknown key' errinfix lineno
End
End
/* Close file */
Call Stream fname, 'C', 'Close'
Return
/* Send a line to a file or device */
SendLine: Procedure Expose global.
Parse Arg output, str
If Translate(output) \= 'CON' Then
Call LineOut output, str
Else
Say str
Return
/* Display an error message and quit */
Error: Procedure Expose global.
Parse Arg msg
Say global.PRGNAME':' msg
global.ERRORLEVEL = 1
Signal Abort
/* Display program version and quit */
Version: Procedure Expose global.
Say global.PRGNAME global.VERSION global.COPYRIGHT
Signal Abort
/* Display help page and quit */
Help: Procedure Expose global.
Say global.PRGNAME global.VERSION global.COPYRIGHT
Say
Say 'Simple PRinter.'
Say
Say 'Usage:'
Say ' spr FILE ... [ SWITCHES ]'
Say
Say 'You may use `con'' as the file name--this enables piping and re-'
Say 'directing output to spr.'
Say
Say 'Switches:'
Say ' /a=N /starting-from=N /b=N /begin=N'
Say ' /c=N /pitch=N /e=N /end=N'
Say ' /f=STR /footer=STR /fe=STR /even-footer=STR'
Say ' /fo=STR /odd-footer=STR /F=N /footers=N'
Say ' /h=STR /header=STR /he=STR /even-header=STR'
Say ' /ho=STR /odd-header=STR /H=N /headers=N'
Say ' /k[+|-] /keep-long[+|-] /l=N /left=N'
Say ' /n=N /page-length=N /o=STR /output=STR'
Say ' /p=STR /printer=STR /q=N /quality=STR'
Say ' /r=N /right=N /s=N /spacing=N'
Say ' /v /version /? /help'
Signal Abort