+ prints the members of macro or text libraries. This option may be
+ specified if the file is a simulated partitioned data set
+ (filetype MACLIB, TXTLIB, or LOADLIB). If an asterisk (*) is
+ entered, all individual members of that library are printed. If a
+ membername is specified, only that member is printed.
+
+HEX
+ prints the file in graphic hexadecimal format. If HEX is speci-
+ fied, the options CC and UPCASE are ignored, even if specified,
+ and even if the filetype is LISTING, LIST3800, LISTCPDS, LIST3820,
+ or LIST38PP. If both the OVersize and HEX options are specified,
+ the NOCC option will be in effect.
+
+ */
+
+
+
+/* -- Here is a version of STANDARD that does not loop, requires
+ pipes mod level 6 (for sync) */
+STANDARD2:
+header = '19'x || title
+
+ 'CALLPIPE (end \)',
+ '| literal' '89'x, /* page eject record? */
+ '| append var header', /* page header record */
+ '| spec 1-* 1', /* simulate BLOCK LINEND by */
+ ' .15. x2c next', /* adding linend chars & join*/
+ '| join *', /* both recs into 1 rec */
+ '| dup *', /* make an endless supply */
+ ,
+ '| a: sync', /* 2 streams marching together */
+ '| b: faninany', /* combine the streams */
+ ,
+ '| deblock linend', /* deblock into separate recs */
+ '| uro' dev, /* print it */
+ ,
+ '\ *:', /* incoming records */
+ '| spec .09. x2c 1', /* add charriage control and */
+ ' 1-* next', /* simulate BLOCK LINEND by */
+ ' .15. x2c next', /* adding linend chars & */
+ '| join' linecount-1, /* join all recs for a page into 1 rec */
+ '| a:', /* send through sync */
+ '| b:' /* sync sends them back, now send them to faninany */
+
+Return
+
diff --git a/vmworkshop-vmarcs/1996/mime/print.xedit b/vmworkshop-vmarcs/1996/mime/print.xedit
new file mode 100644
index 0000000..9877a6b
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/mime/print.xedit
@@ -0,0 +1,27 @@
+/*
+ * Name: PRINT XEDIT
+ * feed this file to your virtual printer via Pipes
+ * Author: Rick Troth, Rice University, I/S VM Systems Support
+ * Date: 1992-Jun-11, Jun-25
+ */
+
+Parse Arg args '(' opts
+
+If args ^= "" Then
+ 'COMMAND CMS PIPE CMS PRINT' args '(' opts '| STEM EMSG.'
+
+Else Do
+ 'COMMAND EXTRACT/LINE/FNAME/FTYPE/FMODE'
+ 'COMMAND TOP'
+ 'COMMAND CMS PIPE XEDIT' ,
+ '| PRINT' fname.1 ftype.1 fmode.1 '(' opts ,
+ '| STEM EMSG.'
+ 'COMMAND :' || line.1
+ rc = 0
+ End /* Else Do */
+
+If rc = 0 Then Do i = 1 To emsg.0; 'COMMAND MSG' emsg.i; End
+ Else Do i = 1 To emsg.0; 'COMMAND EMSG' emsg.i; End
+
+Exit
+
diff --git a/vmworkshop-vmarcs/1996/mime/richtext.rexx b/vmworkshop-vmarcs/1996/mime/richtext.rexx
new file mode 100644
index 0000000..f2edcf4
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/mime/richtext.rexx
@@ -0,0 +1,174 @@
+/*
+ * Name: RICHTEXT REXX
+ * convert MIME "richtext" ("enriched" text) to plain-text.
+ */
+
+'STREAMSTATE OUTPUT 0' /* Have a primary stream? */
+If rc = 12 Then Do
+ 'ADDPIPE *.OUTPUT.0: | CONSOLE'
+ End /* If .. Do */
+
+'STREAMSTATE OUTPUT 1' /* Have a secondary stream? */
+If rc = -4 Then Do
+ 'ADDSTREAM OUTPUT'
+ 'ADDPIPE *.OUTPUT.1: | HOLE'
+ End /* If .. Do */
+
+meta = "TEST_VAR"
+data = "test value"
+'CALLPIPE VAR DATA | SPEC /' ,
+ || meta || '/ 1 /=/ NEXT 1-* NEXT | *.OUTPUT.1:'
+
+meta = "" /* start with NOT collecting meta data */
+data = "" /* start with an EMPTY meta-data buffer */
+
+qs = "> " /* quote prefix string */
+xs = "> " /* excerpt prefix string */
+indent = 4
+
+'CALLPIPE COMMAND QUERY DISPLAY | VAR DISPLAY'
+Parse Var display . . width .
+If ^Datatype(width,'N') Then width = 72
+ Else width = width - 8
+
+/* what about TAB characters? */
+'ADDPIPE *.INPUT: | UNTAB -8 | SPLIT BEFORE /' ,
+ '| SPLIT BEFORE /&/ | SPEC 1-* 1 / / NEXT | *.INPUT:'
+
+line = ""
+center = 0
+quote = 0
+excerpt = 0
+li = 0
+ri = 0
+hs = 0
+
+Do Forever
+
+ 'PEEKTO TEXT'
+ If rc ^= 0 Then Leave
+
+ If Strip(text) = "" Then text = ""
+
+ If Left(text,1) = '<' Then Do
+ Parse Var text '<'command'>'text
+ Parse Upper Var command verb args
+ If Right(text,2) = "= " Then text = Left(text,Length(text)-2)
+
+ Select /* verb */
+
+ When Left(verb,1) = "!" Then nop
+ When verb = "LT" Then line = line || '<'
+ When verb = "GT" Then line = line || '>'
+ When verb = "CENTER" Then center = 1
+ When verb = "/CENTER" Then center = 0
+ When verb = "QUOTATION" Then quote = 1
+ When verb = "/QUOTATION" Then quote = 0
+/* what about BLOCKQUOTE? is it = EXCERPT? */
+ When verb = "EXCERPT" Then Do
+ Call FLUSH
+ 'OUTPUT'
+ excerpt = 1
+ End /* When .. Do */
+ When verb = "/EXCERPT" Then Do
+ Call FLUSH
+ 'OUTPUT'
+ excerpt = 0
+ End /* When .. Do */
+ When verb = "LEFTINDENT" Then li = li + indent
+ When verb = "/LEFTINDENT" Then li = li - indent
+ When verb = "RIGHTINDENT" Then ri = ri + indent
+ When verb = "/RIGHTINDENT" Then ri = ri - indent
+ When verb = "NL" | verb = "BR" Then Call FLUSH
+ When verb = "LI" Then Call FLUSH
+ When verb = "MENU" Then Call FLUSH
+ When verb = "/MENU" Then Call FLUSH
+ When verb = "P" Then Do
+ 'OUTPUT' " "
+ Call FLUSH
+ End /* When .. Do */
+
+ When verb = "ITALIC" | verb = "/ITALIC" ,
+ | verb = "BOLD" | verb = "/BOLD" ,
+ | verb = "FIXED" | verb = "/FIXED" Then nop
+
+ When verb = "TITLE" Then Do
+ If data ^= "" Then 'CALLPIPE VAR DATA | SPEC /' ,
+ || meta || '/ 1 /=/ NEXT 1-* NEXT | *.OUTPUT.1:'
+ meta = "TITLE"
+ End /* When .. Do */
+ When verb = "/TITLE" Then Do
+ If data ^= "" Then 'CALLPIPE VAR DATA | SPEC /' ,
+ || meta || '/ 1 /=/ NEXT 1-* NEXT | *.OUTPUT.1:'
+ meta = ""
+ data = ""
+ End /* When .. Do */
+
+When verb = "HTML" | verb = "/HTML" ,
+ | verb = "HEAD" | verb = "/HEAD" ,
+ | verb = "BODY" | verb = "/BODY" ,
+ | verb = "ADDRESS" | verb = "/ADDRESS" ,
+ | verb = "LINK" | verb = "IMG" ,
+ | verb = "H1" | verb = "/H1" ,
+ | verb = "H2" | verb = "/H2" ,
+ | verb = "H3" | verb = "/H3" ,
+ | verb = "UL" | verb = "/UL" ,
+ | verb = "TT" | verb = "/TT" ,
+ | verb = "I" | verb = "/I" ,
+ | verb = "A" | verb = "/A" Then nop
+
+ Otherwise ,
+ Address "COMMAND" 'XMITMSG 3 VERB (ERRMSG'
+
+ End /* Select verb */
+
+ End /* If .. Do */
+
+ If Left(text,1) = '&' & Index(text,';') > 0 Then Do
+ Parse Var text '&'token';'text
+ Select /* token */
+ When token = "lt" Then text = '<' || text
+ When token = "gt" Then text = '>' || text
+ When token = "thorn" Then text = 'AE'x|| text
+ Otherwise text = '?' || text
+ End /* Select token */
+ End /* If .. Do */
+
+ If ^hs Then text = Strip(text,'L')
+ If meta ^= "" Then data = data || text
+ Else line = line || text
+
+ 'READTO'
+
+ End /* Do Forever */
+
+Call FLUSH
+
+Exit
+
+/* ------------------------------------------------------------------ */
+FLUSH:
+
+w = width - li
+Do While Length(line) > w & Index(line,' ') > 0
+ p = Lastpos(' ',line,w)
+ part = Left(line,p-1)
+ If center Then part = Center(part,width)
+ If quote Then part = qs || part
+ If excerpt Then part = xs || part
+ If li > 0 Then part = Copies(' ',li) || part
+ 'OUTPUT' part
+ line = Substr(line,p+1)
+ End /* Do While */
+
+If center Then line = Center(line,width)
+If quote Then line = qs || line
+If excerpt Then line = xs || line
+If li > 0 Then line = Copies(' ',li) || line
+If line = "" Then line = " "
+
+'OUTPUT' line
+line = ""
+
+Return
+
diff --git a/vmworkshop-vmarcs/1996/mime/uftxread.rexx b/vmworkshop-vmarcs/1996/mime/uftxread.rexx
new file mode 100644
index 0000000..6605df9
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/mime/uftxread.rexx
@@ -0,0 +1,73 @@
+/*
+ * Name: UFTXREAD REXX
+ * Pipelines stage to interpret SIFT/UFT jobs
+ * as extracted from the mailbox
+ * Author: Rick Troth, Rice University, Information Systems
+ * Date: 1993-Apr-07 and prior
+ */
+
+pipe = ""
+name = "mime.text"
+
+Do Forever
+
+ 'PEEKTO LINE'
+ If rc ^= 0 Then Leave
+
+ Parse Upper Var line cmnd .
+ Select /* cmnd */
+ When cmnd = "FILE" Then Parse Var line . size from .
+ When cmnd = "SIZE" Then Parse Var line . size .
+ When cmnd = "USER" Then nop
+ When cmnd = "DATE" Then nop
+ When cmnd = "TYPE" Then Do
+ Parse Var line . type .
+ Select /* type */
+ When type = "A" Then pipe = ,
+ 'CHANGE /' || '0D0A'x || '/' || '0A'x || '/' ,
+ '| DEBLOCK LINEND 0A | DROP LAST | A2E'
+ When type = "E" Then pipe = 'DEBLOCK LINEND 15 | DROP'
+ When type = "V" Then pipe = 'DEBLOCK CMS'
+ When type = "N" Then pipe = 'DEBLOCK NETDATA' ,
+ '| LOCATE' '00C000'x '| SPEC 2-* 1'
+ Otherwise pipe = 'FBLOCK 80' /* binary */
+ End /* Select code */
+ End /* When .. Do */
+ When cmnd = "NAME" Then Parse Var line . name
+
+ When cmnd = "CLASS" Then Parse Var line . class .
+ When cmnd = "FORM" Then Parse Var line . form .
+ When cmnd = "DEST" Then Parse Var line . dest .
+ When cmnd = "DIST" Then Parse Var line . dist .
+ When cmnd = "FCB" Then Parse Var line . fcb .
+ When cmnd = "CTAPE" Then Parse Var line . fcb .
+ When cmnd = "UCS" Then Parse Var line . ucs .
+ When cmnd = "CHARS" Then Parse Var line . ucs .
+ When cmnd = "TRAIN" Then Parse Var line . ucs .
+
+ When cmnd = "DATA" Then Leave
+ Otherwise Say "command/parameter" cmnd "ignored"
+ End /* Select cmnd */
+
+ 'READTO'
+
+ End /* Do While */
+
+If rc ^= 0 Then Exit rc * (rc ^= 12)
+
+'READTO'
+
+If pipe = "" Then 'CALLPIPE *: | > UFTXREAD CMSUT1 A3'
+ Else 'CALLPIPE *: |' pipe '| > UFTXREAD CMSUT1 A3'
+
+If Index(name,'"') > 0 Then
+ Parse Var name . '"' name '"' .
+Parse Var name fn '.' ft '.' .
+If fn = "" Then Parse Var from fn '.' . '@' .
+Push "COMMAND SET FN" fn
+Push "COMMAND SET FT" ft
+Push "COMMAND SET FM A1"
+Address "COMMAND" 'XEDIT UFTXREAD CMSUT1 A3'
+
+Return rc
+
diff --git a/vmworkshop-vmarcs/1996/mime/untab.rexx b/vmworkshop-vmarcs/1996/mime/untab.rexx
new file mode 100644
index 0000000..3398e6c
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/mime/untab.rexx
@@ -0,0 +1,32 @@
+/*
+ * Name: EXPAND REXX
+ * Expand Tab Characters function as a pipeline filter
+ * This gem can be replaced with UNTAB -8, if available.
+ * Author: Rick Troth, Rice University, Information Systems
+ * Date: 1992-Apr-17, Dec-06
+ */
+
+/* 'CALLPIPE *: | UNTAB -8 | *:' */
+
+Do Forever
+
+ 'PEEKTO LINE'
+ If rc ^= 0 Then Leave
+
+ tabpos = Pos('05'x,line)
+ Do While tabpos > 0
+ line = Substr(line,1,tabpos-1) || ,
+ Copies('40'x,((tabpos+7)%8)*8-tabpos+1) || ,
+ Substr(line,tabpos+1)
+ tabpos = Pos('05'x,line)
+ End /* Do While */
+
+ 'OUTPUT' line
+ If rc ^= 0 Then Leave
+
+ 'READTO'
+
+ End /* Do While */
+
+Exit rc * (rc ^= 12)
+
diff --git a/vmworkshop-vmarcs/1996/mime/webtext.rexx b/vmworkshop-vmarcs/1996/mime/webtext.rexx
new file mode 100644
index 0000000..daf1bdc
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/mime/webtext.rexx
@@ -0,0 +1,178 @@
+/* Copyright 1994, Richard M. Troth
+ *
+ * Name: WEBTEXT REXX
+ * VM TCP/IP Network Client and Server text converter
+ * Inspired by GOPCLITX, DROPDOTS, and others.
+ * To be renamed MAKETEXT because it's ubiquitous.
+ * Author: Rick Troth, Houston, Texas, USA
+ * Date: 1994-Feb-27, 1994-Oct-15
+ *
+ * Replaces: A2E, E2A, TCPA2E, TCPE2A
+ */
+
+/* ----------------------------------------------------------------- ÆCS
+ * ASCII to EBCDIC and vice-versa code conversion tables.
+ * Tables included here are based on ASCII conforming to the ISO8859-1
+ * Latin 1 character set and EBCDIC conforming to the IBM Code Page 37
+ * Latin 1 character set (except for three pairs of characters in 037).
+ */
+
+Parse Upper Arg mode code .
+If mode = "" Then mode = "LOCAL"
+
+ i = '000102030405060708090A0B0C0D0E0F'x
+ i = i || '101112131415161718191A1B1C1D1E1F'x
+ i = i || '202122232425262728292A2B2C2D2E2F'x
+ i = i || '303132333435363738393A3B3C3D3E3F'x
+ i = i || '404142434445464748494A4B4C4D4E4F'x
+ i = i || '505152535455565758595A5B5C5D5E5F'x
+ i = i || '606162636465666768696A6B6C6D6E6F'x
+ i = i || '707172737475767778797A7B7C7D7E7F'x
+ i = i || '808182838485868788898A8B8C8D8E8F'x
+ i = i || '909192939495969798999A9B9C9D9E9F'x
+ i = i || 'A0A1A2A3A4A5A6A7A8A9AAABACADAEAF'x
+ i = i || 'B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF'x
+ i = i || 'C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF'x
+ i = i || 'D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF'x
+ i = i || 'E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF'x
+ i = i || 'F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF'x
+
+If code ^= "" Then Do
+ 'CALLPIPE DISK' code 'TCPXLBIN | STEM XLT.'
+ If rc ^= 0 | xlt.0 < 3 Then code = ""
+ End /* If .. Do */
+
+Select /* mode */
+ When Abbrev("LOCAL",mode,3) Then Call LOCAL
+ When Abbrev("LCL",mode,3) Then Call LOCAL
+ When Abbrev("NETWORK",mode,3) Then Call NETWORK
+ When Abbrev("DOTTED",mode,3) Then Call DOTTED
+ Otherwise Do
+ Address "COMMAND" 'XMITMSG 3 MODE (ERRMSG'
+ rc = 24
+ End /* Otherwise Do */
+ End /* Select mode */
+
+Exit rc * (rc ^= 12)
+
+
+/* --------------------------------------------------------------- LOCAL
+ * Input: raw ASCII text
+ * Output: plain (EBCDIC) text
+ */
+LOCAL:
+
+'ADDPIPE *.OUTPUT: | STRIP TRAILING 0D | PAD 1 | *.OUTPUT:'
+If rc ^= 0 Then Return
+
+If code = "" Then Do /* use the standard table */
+ e = '00010203372D2E2F1605250B0C0D0E0F'x
+ e = e || '101112133C3D322618193F271C1D1E1F'x
+ e = e || '405A7F7B5B6C507D4D5D5C4E6B604B61'x
+ e = e || 'F0F1F2F3F4F5F6F7F8F97A5E4C7E6E6F'x
+ e = e || '7CC1C2C3C4C5C6C7C8C9D1D2D3D4D5D6'x
+ e = e || 'D7D8D9E2E3E4E5E6E7E8E9ADE0BD5F6D'x
+ e = e || '79818283848586878889919293949596'x
+ e = e || '979899A2A3A4A5A6A7A8A9C04FD0A107'x
+ e = e || '202122232415061728292A2B2C090A1B'x
+ e = e || '30311A333435360838393A3B04143EFF'x
+ e = e || '41AA4AB19FB26AB5BBB49A8AB0CAAFBC'x
+ e = e || '908FEAFABEA0B6B39DDA9B8BB7B8B9AB'x
+ e = e || '6465626663679E687471727378757677'x
+ e = e || 'AC69EDEEEBEFECBF80FDFEFBFCBAAE59'x
+ e = e || '4445424643479C485451525358555657'x
+ e = e || '8C49CDCECBCFCCE170DDDEDBDC8D8EDF'x
+ End /* If .. Do */
+Else e = xlt.2
+
+buff = ""
+Do Forever
+
+ 'PEEKTO DATA'
+ If rc ^= 0 Then Leave
+
+ buff = buff || data
+ Do While Index(buff,'0A'x) > 0
+ Parse Var buff line '0A'x buff
+ 'OUTPUT' Translate(line,e,i)
+ If rc ^= 0 Then Leave
+ End /* Do While */
+ If rc ^= 0 Then Leave
+
+ 'READTO'
+ If rc ^= 0 Then Leave
+
+ End /* Do Forever */
+
+If buff ^= "" Then 'OUTPUT' Translate(buff,e,i)
+
+Return
+
+
+/* ------------------------------------------------------------- NETWORK
+ * Input: plain (EBCDIC) text
+ * Output: raw ASCII byte stream
+ */
+NETWORK:
+
+'ADDPIPE *.OUTPUT: | SPEC 1-* 1 x0D0A NEXT | *.OUTPUT:'
+If rc ^= 0 Then Return
+
+If code = "" Then Do /* use the standard table */
+ a = '000102039C09867F978D8E0B0C0D0E0F'x
+ a = a || '101112139D8508871819928F1C1D1E1F'x
+ a = a || '80818283840A171B88898A8B8C050607'x
+ a = a || '909116939495960498999A9B14159E1A'x
+ a = a || '20A0E2E4E0E1E3E5E7F1A22E3C282B7C'x
+ a = a || '26E9EAEBE8EDEEEFECDF21242A293B5E'x
+ a = a || '2D2FC2C4C0C1C3C5C7D1A62C255F3E3F'x
+ a = a || 'F8C9CACBC8CDCECFCC603A2340273D22'x
+ a = a || 'D8616263646566676869ABBBF0FDFEB1'x
+ a = a || 'B06A6B6C6D6E6F707172AABAE6B8C6A4'x
+ a = a || 'B57E737475767778797AA1BFD05BDEAE'x
+ a = a || 'ACA3A5B7A9A7B6BCBDBEDDA8AF5DB4D7'x
+ a = a || '7B414243444546474849ADF4F6F2F3F5'x
+ a = a || '7D4A4B4C4D4E4F505152B9FBFCF9FAFF'x
+ a = a || '5CF7535455565758595AB2D4D6D2D3D5'x
+ a = a || '30313233343536373839B3DBDCD9DA9F'x
+ End /* If .. Do */
+Else a = xlt.3
+
+Do Forever
+
+ 'PEEKTO LINE'
+ If rc ^= 0 Then Leave
+
+ 'OUTPUT' Translate(line,a,i)
+ If rc ^= 0 Then Leave
+
+ 'READTO'
+ If rc ^= 0 Then Leave
+
+ End /* Do Forever */
+
+Return
+
+
+/* -------------------------------------------------------------- DOTTED
+ * Input: plain (EBCDIC) text
+ * Output: ASCII byte stream terminated by CR/LF/./CR/LF
+ */
+DOTTED:
+
+Call NETWORK
+
+'OUTPUT' Translate('.',a,i)
+
+Return
+
+
+/*
+ * variables:
+ * xlt.0 should be "3", meaning three records read
+ * xlt.1 should be a comment
+ * xlt.2 should be our ASCII ---> EBCDIC table
+ * xlt.3 should be our EBCDIC ---> ASCII table
+ * i is set to the dummy input table
+ */
+
diff --git a/vmworkshop-vmarcs/1996/mime/webume.text b/vmworkshop-vmarcs/1996/mime/webume.text
new file mode 100644
index 0000000..6103e85
Binary files /dev/null and b/vmworkshop-vmarcs/1996/mime/webume.text differ
diff --git a/vmworkshop-vmarcs/1996/mod210/mod210.info b/vmworkshop-vmarcs/1996/mod210/mod210.info
new file mode 100644
index 0000000..d40c913
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/mod210/mod210.info
@@ -0,0 +1,1407 @@
+Marist 1996 VM Workshop Tools Tape Contribution, CP mods for VM/ESA 2.1.0
+
+ADDR Command:
+This mod will add a new command to VM/ESA called ADDR. This command will
+display information about a "terminal", including its address, devicetype
+and other info depending on what kind of device it is. It can be used on all
+types of workstations, including SNA devices and logical devices. In the
+case of an SNA device, the LU name will be displayed. If it is run on a
+logical device, the owner of the device will be displayed.
+
+The mod consists of a new module, HCPADR and minor changes to HCPCOM, HCPMDLAT
+COPY and HCPLDL. The change to HCPCOM to define the new command, ADDR. The
+changes to HCPMDLAT COPY and HCPLDL are to add the new module to the system.
+
+This mod was developed on a VM/XA system, and later ported to VM/ESA. The
+current version should run on all levels of VM/ESA ESA feature from 1.1.0
+through 2.1.0. However, future levels shouldn't be a problem either.
+
+Questions or problems with this mod should be directed to Martha McConaghy,
+Marist College, URMM@VM.MARIST.EDU (914) 575-3252.
+
+FILE: HCPCOM ADDRCMD E1
+./ I 02534991 $ 2535000 100 10/31/89 18:31:26
+************************************************************** ADDRCMD 0582671
+* * ADDRCMD 0583470
+* ADDR COMMAND * ADDRCMD 0584269
+* * ADDRCMD 0585068
+************************************************************** ADDRCMD 0585867
+ SPACE , ADDRCMD 0586666
+ADDR DS 0F ADDRCMD 0587465
+ COMMD COMMAND=(ADDR,4),FL=CMDALOG+CMDONLY, *05882640
+ CLASS=*,EP=HCPADRES ADDRCMD 0589063
+ SPACE , ADDRCMD 0589862
+
+
+FILE: HCPMDLAT ADDRCMD E1 1
+./ I 61920002 $ 61920100 100 10/30/89 10:51:36
+ AIF ('&NAME'(1,6) NE 'HCPADR' AND (NOT &HCPLLST) )*31781000
+ .EHCPADR ADDRCMD 3178200
+ HCPATTRB HCPADR,MODATTR=(PAG,MP,DYN), *31783000
+ EP=((HCPADRES,DYN)) ADDRCMD 3178400
+ AIF ('&HCPATTRC' EQ '0').MDLATEX IF FOUND, EXIT ADDRCMD 3178500
+.EHCPADR ANOP , ADDRCMD 3178600
+.**************************************************************ADDRCMD 3178000
+
+FILE: HCPLDL ADDRCMD E1
+./ * CO-REQ: HCPADR
+./ * IF-REQ: NONE
+./ * FORCE REASSEMBLY FOR HCPADR MOD FOR HCPMDLAT MACRO
+
+FILE: HCPADR ASSEMBLE E1
+ADR TITLE 'HCPADR (CP) VM/ESA R 1.0' 00000100
+ ISEQ 73,80 VALIDATE SEQUENCING OF INPUT 00000200
+ COPY HCPOPTNS 00000300
+HCPADR HCPPROLG ATTR=(PAGEABLE,REENTERABLE),BASE=(R12) 00000400
+* 00000500
+* MODULE NAME - HCPADR 00000600
+* 00000700
+* DESCRIPTIVE NAME - ADDR COMMAND 00000800
+* 00000900
+* STATUS - VM/ESA 1.0 ESA 00001000
+* 00001100
+* FUNCTION - To process the ADDR command. 00001200
+* 00001300
+* NOTES - 00001400
+* 00001500
+* DEPENDENCIES - THIS MODULE REQUIRES THE USE OF THE IBM SYS 00001600
+* 370 - XA SERIES OF PROCESSORS RUNNING IN 370 00001700
+* MODE. 00001800
+* 00001900
+* REGISTER CONVENTIONS - SYMBOLIC REFERENCES TO REGISTERS AR 00002000
+* THE FORM "RX" WHERE X IS A NUMBER R 00002100
+* FROM 0 TO 15. 00002200
+* 00002300
+* MODULE TYPE - PROCEDURE 00002400
+* 00002500
+* PROCESSOR - ASSEMBLER H, VERSION 2 R1 00002600
+* 00002700
+* ATTRIBUTES - REENTRANT, PAGEABLE 00002800
+* 00002900
+* ENTRY POINT - 00003000
+* 00003100
+* HCPADRES - PROCESS THE ADDR COMMAND 00003200
+* 00003300
+* EXTERNAL REFERENCES - 00003400
+* 00003500
+* ROUTINES - 00003600
+* 00003700
+* HCPCVTBH - To convert binary to hex 00003800
+* HCPLDAFE - Fetch the LDD for a logical device LDEV 00003830
+* HCPLDARE - Return LDD to system LDEV 00003860
+* HCPLSOTR - Translate the device number into hex 00003900
+* 00004100
+* DATA AREAS - 00004200
+* ADRMSG - Message response DSECT 00004300
+* 00004400
+* CONTROL BLOCKS - 00004500
+* HCPCSLPL - REQUIRED FOR HCPCONSL MACRO 00004550
+* HCPEQUAT - SYSTEM EQUATES 00004600
+* HCPPFXPG - PREFIX AREA PAGE 00004700
+* HCPRDCBK - REAL DEVICE CHARACTERISTIC BLOCK 00004800
+* HCPRDEV - REAL DEVICE BLOCK 00004900
+* HCPSAVBK - SAVE AREA 00005000
+* HCPSYSCM - SYSTEM COMMON AREA 00005100
+* HCPVMDBK - INVOKER'S VMDBK 00005200
+* 00005300
+* MACROS - 00005400
+* HCPCALL - STANDARD SYSTEM CALLING LINKAGE 00005500
+* HCPCONSL - Write results to the user's console ADCONSL 00005550
+* HCPDROP - RELEASE ADDRESSABILITY 00005600
+* HCPENTER - DEFINITION OF EXECUTABLE ENTRY POINT 00005700
+* HCPEPILG - GENERATE MODULE EPILOG 00005800
+* HCPEXIT - RETURN TO CALLER 00005900
+* HCPGETST - GET FREE STORAGE 00006000
+* HCPRELST - RELEASE FREE STORAGE 00006100
+* HCPUSING - ESTABLISH ADDRESSABILITY 00006200
+* 00006300
+* ABEND CODES - NONE 00006400
+* 00006500
+* RESPONSE - 00006600
+* DEVICE XXXXX ON SYSTEM VVVVVVVV TYPE TTTTTTTT 00006700
+* or LDEV 00006860
+* Device XXXXX on System VVVVVVVV Type TTTTTTTT Owner OOOOOLDEV 00006920
+* for logical devices LDEV 00006980
+* 00007100
+* 00007200
+ SPACE 3 00007300
+ EXTRN HCPLDAFE Fetch LDD for logical devs LDEV 00007450
+ EXTRN HCPLDARE Return LDD for logical devs LDEV 00007500
+ EXTRN HCPLSOTR Translate raddr for printing LDEV 00007550
+ EXTRN HCPCVTBH Convert binary to ebcdic LDEV 00007600
+* 00007700
+ COPY HCPCSLPL Console parameter list LDEV 00007820
+ COPY HCPPFXPG Prefix page LDEV 00007890
+ COPY HCPSYSCM System common block LDEV 00007960
+ COPY HCPSAVBK Save area LDEV 00008030
+ COPY HCPEQUAT Common equates LDEV 00008100
+ COPY HCPVMDBK VMDBK LDEV 00008170
+ COPY HCPRDEV Real Device block LDEV 00008240
+ COPY HCPSNABK SNA device block LDEV 00008310
+ COPY HCPRDCBK Real dev characteristics block LDEV 00008380
+ EJECT 00008600
+ HCPUSING PFXPG,0 00008700
+ HCPUSING VMDBK,R11 00008800
+ HCPUSING SAVBK,R13 00008900
+* 00009000
+HCPADRES HCPENTER CALL,SAVE=DYNAMIC 00009100
+* 00009200
+* Get storage for response buffer and set up text. LDEV 00009250
+ LA R0,ADRMSGDL GET SIZE OF RESPONSE 00009300
+ HCPGETST LEN=(R0) GET STORAGE BLOCK 00009400
+ LR R3,R1 Save address of block LDEV 00009540
+ HCPUSING ADRMSG,R3 00009600
+ MVI ADRMSG,C' ' CLEAR OUT RESPONSE BUFF 00009700
+ MVC ADRMSG1(ADRMSGDL*8-1),ADRMSG 00009800
+ MVC TXT1,=C'Device ' Insert text into message LDEV 00009940
+ MVC TXT2,=C' on system ' 00010000
+ MVC TXT3,=C' type ' 00010100
+* 00010200
+* Now, find the RDEV block and determine if it is a SNA device.LDEV 00010250
+ L R8,VMDRTERM GET ADDRESS OF RDEVBLOK 00010300
+ HCPUSING RDEV,R8 00010400
+ ICM R1,B'1111',RDEVSNA IS THERE A SNABK BLOCK? 00010500
+ BNZ SNADEV Yes then get SNA info 00010600
+* LDEV 00010710
+* For non-SNA devices, get raddr and device type. LDEV 00010720
+ LA R1,RADDR Set location of buffer LDEV 00010730
+ HCPCALL HCPLSOTR GET ADDRESS OF TERMINAL 00010800
+ L R7,RDEVRDCA GET DEVICE CHAR BLOCK 00010900
+ HCPUSING RDCBK,R7 00011000
+ SR R1,R1 CLEAR OUT REG 00011100
+ MVC DEVTYP(2),RDCDVID Get device type LDEV 00011240
+ LH R1,DEVTYP 00011300
+ HCPCALL HCPCVTBH Convert it to EBCDIC LDEV 00011410
+ STM R0,R1,DEVTYP Put back in buffer LDEV 00011420
+* LDEV 00011430
+* Determine if we are on a logical device. If so, then LDEV 00011440
+* get the owner of the device too. LDEV 00011450
+ L R1,RDEVLSOP Get pointer to LSOBJ LDEV 00011460
+ LTR R1,R1 Are we on a LDEV? LDEV 00011470
+ BZ SYSTEMID NO, then finish up LDEV 00011480
+ LH R1,RDEVDEV Get LDEV number LDEV 00011490
+* The call to HCPLDAFE locks the LDDBK for the logical device.LDEV 00011500
+* It must be released before leaving the module. LDEV 00011510
+ HCPCALL HCPLDAFE Fetch LDDBK for device LDEV 00011520
+ LTR R15,R15 Did we find one? LDEV 00011530
+ BNZ SYSTEMID NO, something's wrong LDEV 00011540
+ LR R4,R2 Save VMDBK addr for owner LDEV 00011550
+ HCPCALL HCPLDARE Release lock on LDDBK LDEV 00011560
+ MVC LOWNER,VMDUSER-VMDBK(R4) Get owner's ID LDEV 00011570
+ MVC TXT4,=C' Owner ' LDEV 00011580
+ B SYSTEMID 00011600
+* 00011700
+* If user is on a SNA device, set the device type to SNA LDEV 00011730
+* and set address to LU name. LDEV 00011760
+SNADEV L R1,RDEVSNA Get address of SNABK 00011800
+ HCPUSING SNABK,R1 00011900
+ MVC RADDR,SNALUN Move in the LU name 00012000
+ MVC DEVTYP,=C' SNA ' 00012100
+ HCPDROP R1 00012200
+* 00012300
+* Finish up by setting the system ID field. LDEV 00012350
+SYSTEMID L R1,PFXSYS 00012400
+ HCPUSING SYSCM,R1 00012500
+ MVC SYSID,SYSTMID Get system id from SYSCM LDEV 00012640
+ HCPDROP R1 00012700
+* 00012800
+* Write the message to the user's console and release the LDEV 00012860
+* storage. LDEV 00012920
+PRTMSG DS 0H ADCONSL 00012990
+ HCPCONSL WRITE,DATA=((R3),ADRMSGL) ADCONSL 00013080
+ HCPRELST BLOCK=(R3) ADCONSL 00013170
+ HCPEXIT EP=(HCPADRES),SETCC=NO 00013400
+ EJECT 00013500
+* LDEV 00013530
+* Define block for resulting message. LDEV 00013560
+ADRMSG DSECT 00013600
+TXT1 DS CL8 "DEVICE " LDEV 00013740
+RADDR DS CL8 00013800
+TXT2 DS CL12 " ON SYSTEM " 00013900
+SYSID DS CL8 00014000
+TXT3 DS CL8 " TYPE " 00014100
+DEVTYP DS CL8 00014300
+TXT4 DS CL8 " OWNER " LDEV 00014330
+LOWNER DS CL8 LDEV 00014360
+ADRMSGL EQU (*-ADRMSG) LENGTH OF RESPONSE 00014400
+ADRMSGDL EQU (*-ADRMSG+7)/8 LENGTH OF RES IN DW 00014500
+ADRMSG1 EQU ADRMSG+1 00014600
+ EJECT 00014700
+HCPADR CSECT 00014800
+ LTORG 00014900
+ HCPDROP R3,R7,R8,R11,R13 00015000
+ HCPEPILG 00015100
+
+*******************************************************************************
+Reformat QUERY NAMES command:
+
+The purpose of this mod is to reformat the output of the QUERY NAMES command.
+The results of this command on a vanilla system includes the address of
+where the virtual machine is running or 'DSC' for disconnected machines.
+On a large system, such as a 9672-R42E, this can generate several screens
+worth of data and is confusing to unsophisticated users. This mod removes
+the address from the display, and shows 8 virtual machine names on each line.
+This greatly decreases the size of the display.
+
+In addition, this mod adds several new options to the command. The 'ALL'
+option shows all virtual machines running on the system, whether they
+are connected or not. This is the default. The 'DISCONNECT' option shows
+only those machines that are running disconnected, while 'CONNECT' shows
+only connected machines. Finally, 'SNA' shows only those machines that
+are logged onto the system via a *VSM connection (ie. VTAM or TCPIP).
+
+The following is an example of how the QUERY NAMES command looks after
+this mod:
+
+query names all
+
+STSS KMMQ STMQ URCG URBP UICOMM SYS$MMM SYS$DKB
+URDB UIHD JZEM MUSICB URLS URMJ KK3R HDPOPR1
+XAMWRITE MUSICC MUSICA SERVDIR HDPCNTRL VMNET MARISTA MARISTC
+MARISTB SPSSBAT PRT3820 PDMREM1 PDMGRP3 SFCM RSCS MAILER
+LISTSERV SIM3278 SQL2DBA NETSERV RSCSX GCS AUTOLINK OPERSYMP
+URMM2
+
+query names disconnect
+
+STSS KMMQ STMQ JZEM MUSICB XAMWRITE MUSICC MUSICA
+HDPCNTRL VMNET MARISTA MARISTC MARISTB SPSSBAT PRT3820 PDMREM1
+PDMGRP3 SFCM RSCS MAILER LISTSERV SIM3278 SQL2DBA NETSERV
+RSCSX GCS AUTOLINK OPERSYMP
+
+query names connect
+
+URCG URBP UICOMM SYS$MMM SYS$DKB URDB UIHD URLS
+URMJ KK3R HDPOPR1 SERVDIR URMM2
+
+query names sna
+
+VSM - VTAM
+URCG -LM020B2A
+VSM - TCPIPA
+VSM - TCPIPB
+VSM - TCPIP
+VSM - TCPIPC
+
+The mod contained in this file is running on VM/ESA ESA 1.0.
+Problems, questions or comments should be sent to the mod developer,
+Martha McConaghy URMM@MARIST (BITNET) or URMM@VM.MARIST.EDU (Internet)
+Senior VM Systems Programmer
+Marist College
+Poughkeepsie, NY 12601
+(914) 575-3252
+6/24/96
+
+FILE: HCPCQU QNAMES E1
+./ R 02540001 $ 2544991 4990 02/15/96 10:54:59
+* 2. PLACE FIELD IN BUFFER EIGHT TO A LINE. * QNAMES 02544991
+./ R 02612401 02616001 $ 2612691 290 02/15/96 10:54:59
+* +----------+--------------------+ * QNAMES 02612691
+* | QUERY | NAMES | * QNAMES 02612981
+* | QUERY | | * QNAMES 02613271
+* | QUERY | | * QNAMES 02613561
+* | QUERY | | * QNAMES 02613851
+* +----------+--------------------+ * QNAMES 02614141
+* ALL - PRINTS ALL CONNECTED AND DISCONNECTED USERS * QNAMES 02614431
+* DISCONN- PRINTS ONLY DISCONNECTED USERS * QNAMES 02614721
+* CONNECT- PRINTS ONLY CONNECTED USERS * QNAMES 02615011
+* SNA - PRINTS ALL SNA USERS W/LUNAMES * QNAMES 02615301
+* * QNAMES 02615591
+./ R 02640001 02660001 $ 2646991 6990 02/15/96 10:54:59
+* USERID USERID USERID USERID USERID USERID USERID USERID * QNAMES 02646991
+* ... * QNAMES 02653981
+* - * QNAMES 02660971
+./ I 02930001 $ 2932001 2000 02/15/96 10:54:59
+ LA R0,1(0) GET BLOCK FOR QNAMOP QNAMES 02932001
+ HCPGETST LEN=(R0) QNAMES 02934001
+ LR R9,R1 QNAMES 02936001
+ HCPUSING QNAMOP,R9 QNAMES 02938001
+./ R 02942001 02960001 $ 2942991 990 02/15/96 10:54:59
+ BNZ SETDEF NO OPERANDS? SET TO 'ALL' QNAMES 02942991
+ LR R6,R0 SAVE LENGTH QNAMES 02943981
+ BCTR R6,0 QNAMES 02944971
+* CHECK OPERANDS QNAMES 02945961
+ EX R6,CLCALL QNAMES 02946951
+ BE SETDEF QNAMES 02947941
+ EX R6,CLCDISC QUERY DISCONNECTED USERS? QNAMES 02948931
+ BNE CONNECT NO, NEXT ONE QNAMES 02949921
+ MVI QNAMFLG,QDISC SET DISCONNECT FLAG QNAMES 02950911
+ B QRYSTRT GO DO IT QNAMES 02951901
+CONNECT EX R6,CLCCONN QUERY CONNECTED USERS? QNAMES 02952891
+ BNE SNAUSRS NO, NEXT ONE QNAMES 02953881
+ MVI QNAMFLG,QCONN SET CONNECT FLAG QNAMES 02954871
+ B QRYSTRT GO DO IT QNAMES 02955861
+SNAUSRS EX R6,CLCSNA QUERY SNA USERS? QNAMES 02956851
+ BNE SETDEF NO, THEN DO ALL ANYWAY QNAMES 02957841
+ MVI QNAMFLG,QSNA SET SNA FLAG QNAMES 02958831
+ B QRYSTRT GO DO IT QNAMES 02959821
+SETDEF MVI QNAMFLG,QALL SET ALL FLAG QNAMES 02960811
+QRYSTRT DS 0H QNAMES 02961801
+./ I 04180001 $ 4183001 3000 02/15/96 10:54:59
+ TM QNAMFLG,QSNA DISPLAY SNA USERS QNAMES 04183001
+ BO QRYSNA QNAMES 04186001
+./ R 04310001 04320001 $ 4314991 4990 02/15/96 10:54:59
+ BZ CHKDISC NO, SO THIS IS A NON-SNA USER QNAMES 04314991
+ TM VMDOSTAT,VMDDISC QNAMES 04319981
+ BO CHKDISC QNAMES 04324971
+./ R 04350001 $ 4354991 4990 02/15/96 10:54:59
+ BZ CHKCONN NO, SO THIS IS A NON-SNA USER QNAMES 04354991
+./ R 04370001 04380001 $ 4375991 5990 02/15/96 10:54:59
+ BNE CHKCONN NOPE - REGULAR Q NAMES QNAMES 04375991
+DONXT C R10,VMDORIG-VMDBK(,R11) END IF UNDER SYS VMDBK QNAMES 04381981
+./ R 04430001 04480001 $ 4437991 7990 02/15/96 10:54:59
+*QRYNISF1 DS 0H QNAMES 04437991
+* CL R10,VMDORIG-VMDBK(,R11) BACK TO START? QNAMES 04445981
+* BE QRYMSGL YES - GO STACK GSDBK AND GET SNA QNAMES 04453971
+* PART OF THE RESPONSE QNAMES 04461961
+* L R10,VMDCYCLE LOAD NEXT VMDBK ADDRESS QNAMES 04469951
+* B QRYCONT CHECK IF THIS IS A NON-SNA USER QNAMES 04477941
+./ I 04500001 $ 4500901 900 02/15/96 10:54:59
+CHKDISC TM QNAMFLG,QALL CHECK FOR 'ALL' OR 'DISC' QNAMES 04500901
+ BO QRYGETID QNAMES 04501801
+ TM QNAMFLG,QDISC QNAMES 04502701
+ BO QRYGETID QNAMES 04503601
+ B DONXT QNAMES 04504501
+CHKCONN TM QNAMFLG,QALL CHECK FOR 'ALL' OR 'CONN' QNAMES 04505401
+ BO QRYGETID QNAMES 04506301
+ TM QNAMFLG,QCONN QNAMES 04507201
+ BO QRYGETID QNAMES 04508101
+ B DONXT QNAMES 04509001
+./ R 04620001 $ 4620991 990 02/15/96 10:54:59
+ CLC VMDUSER(4),=C'LOGN' IS IT A PRE-LOGON? QNAMES 04620991
+ BE QRYGTID3 YES, THEN GET NEXT USERID QNAMES 04621981
+ CLC VMDUSER(4),=C'LOGL' IS IT A PRE-LOGON? QNAMES 04622971
+ BE QRYGTID3 QNAMES 04623961
+ CLC VMDUSER(4),=C'LOGV' IS IT A SNA PRELOGON? QNAMES
+ BE QRYGTID3 QNAMES
+ MVC 0(L'VMDUSER,R6),VMDUSER GET USERID QNAMES 04624951
+./ R 04810001 04830001 $ 4816991 6990 02/15/96 10:54:59
+* BZ QYGTID2A LOCAL OR LOGICAL IS OK. QNAMES 04816991
+* CL R10,VMDORIG-VMDBK(,R11) BACK TO START? QNAMES 04823981
+* BE QRYMSGL YES - GO STACK GSDBK AND GET SNA *04830971
+./ R 04990001 05000001 $ 4995991 5990 02/15/96 10:54:59
+ MVI L'VMDUSER(R6),C' ' SPACE OVER QNAMES 04995991
+ LA R6,9(R6) BUMP TO NEXT SEGMENT QNAMES 05001981
+./ R 05210001 $ 5212991 2990 02/15/96 10:54:59
+ TM QNAMFLG,QSNA DISPLAY SNA USERS? QNAMES 05212991
+ BNO QRYEXIT GET SNA PART OF THE RESPONSE QNAMES 05215981
+./ I 05250001 $ 5252001 2000 02/15/96 10:54:59
+ TM QNAMFLG,QSNA DISPLAY SNA USERS? QNAMES 05252001
+ BO QRYSNA GET SNA PART OF RESPONSE QNAMES 05254001
+ HCPRELST BLOCK=(R9) QNAMES 05256001
+ B QRYEXIT QNAMES 05258001
+./ I 05260001 $ 5265001 5000 02/15/96 10:54:59
+ HCPDROP R9 QNAMES 05265001
+./ I 05370001 $ 5371001 1000 02/15/96 10:54:59
+ HCPRELST BLOCK=(R9) QNAMES 05371001
+./ I 05930001 $ 5935001 5000 02/15/96 10:54:59
+ HCPUSING NRESP,R6 QNAMES 05935001
+./ I 06340001 $ 6342001 2000 02/15/96 10:54:59
+CLCALL CLC 0(*-*,R1),=C'ALL ' QNAMES 06342001
+CLCDISC CLC 0(*-*,R1),=C'DISCONN ' QNAMES 06344001
+CLCCONN CLC 0(*-*,R1),=C'CONNECT ' QNAMES 06346001
+CLCSNA CLC 0(*-*,R1),=C'SNA ' QNAMES 06348001
+./ R 15680001 $ 15684991 4990 02/15/96 10:54:59
+QRYNCNT EQU 8 8 QUERY NAMES PER LINE QNAMES 15684991
+./ I 16290001 $ 16290801 800 02/15/96 10:54:59
+************************************************************ QNAMES 16290801
+* DSECT FOR DETERMINING TYPE OF Q NAMES OPERANDS * QNAMES 16291601
+************************************************************ QNAMES 16292401
+ SPACE 2 QNAMES 16293201
+QNAMOP DSECT QNAMES 16294001
+QNAMFLG DS X OPERAND FLAG QNAMES 16294801
+QALL EQU X'0F' PRINT ALL OF THE USERS QNAMES 16295601
+QDISC EQU X'08' PRINT ONLY DISCONNECT USERS QNAMES 16296401
+QCONN EQU X'04' PRINT ONLY CONNECT USERS QNAMES 16297201
+QSNA EQU X'82' PRINT ONLY SNA USERS QNAMES 16298001
+ EJECT QNAMES 16298801
+
+*****************************************************************************
+Secure QUERY NAMES command:
+
+This mod implements a more 'secure' version of the QUERY NAMES command.
+It allows you to hide certain virtual machines from view in the results
+of the QUERY NAMES. It does this by defining a new OPTION in the directory,
+SECURE. When the QUERY NAMES command is processed, it will not display the
+names of virtual machines with the SECURE option, unless the invoker
+has class A, C or E.
+
+This mod has two benefits. First, the less curious students see, the better.
+This is obviously not a foolproof security system and is not meant to prevent
+hackers from breaking into a guest machine. However, it also does not present
+them with temptation. The addition of the SECURE option also implies other
+future possibilities. For example, the 'QUERY vmname' command could be
+modified to respect the option.
+
+A second, unexpected benefit was to 'clean up' the QUERY NAMES display.
+Normal, class G users, such as our students, use the QUERY NAMES to see if
+they friends are online. This is made difficult when the results include
+dozens of service machines, guest systems, etc. By using the SECURE option,
+the general users see only a list of other users or service machines to
+which they have access, ie. LISTSERV, etc. We have had very positive
+feedback on this from our users.
+
+This mod consists of two parts. First, it adds the SECURE parameter to
+the OPTION record in the directory. Along with this, it utilizes one of
+the installation flags in the VMDBLK to indicate that the SECURE option
+is on. The second part of the mod, modifies the QUERY NAMES command to
+skip those machines who have SECURE on. This part of the mod is dependent
+on another Marist mod, QNAMES, which reformats the results of the
+QUERY NAMES command. However, the code could be rewritten to work on
+a vanilla version of HCPCQU.
+
+This mod was developed on VM/ESA ESA 1.0 service level 9202. This level
+was modified to run on VM/ESA 2.1.0. Questions
+or problems with it should be directed to the author,
+
+Martha McConaghy URMM@MARIST or URMM@VM.MARIST.EDU
+Senior VM Systems Programmer
+Marist College
+Poughkeepsie, NY 12601
+(914) 575-3252
+6/24/96
+
+
+Moduled changed: HCPCQU
+ HCPDIR
+ HCPLGN
+
+Copy blocks changed: HCPVMDBK
+ HCPDVMD
+
+
+
+FILE: HCPDIR SECQNAM E1
+./ R 54601954 $ 54602000 50 02/14/92 11:14:45
+OPTLEN3C DS 0H LENGTH IS AT LEAST 3 SECQNAM 10339100
+ SPACE 1 SECQNAM 10339110
+ COMP =C'SECURE ',SCOPTCMP IS IT 'SECURE'? SECQNAM 10339120
+ BNE OPTLEN3D NO, CHECK NEXT OPTION SECQNAM 10339130
+ SPACE 1 SECQNAM 10339140
+ OI DVMDMARS,DVMDSEC TURN ON SECURE FLAG BIT SECQNAM 10339150
+ B OPTGET Go scan for the next option SECQNAM 10339160
+ SPACE 1 SECQNAM 10339170
+OPTLEN3D DS 0H SECQNAM 10339180
+
+FILE: HCPDVMD SECQNAM E1
+./ R 02610002 $ 2611000 100 05/13/94 10:41:29
+* DS X RESERVED FOR FUTURE IBM USE SECQNAM 01730001
+DVMDMARS DS 1X USE FOR MARIST FLAGS SECQNAM
+DVMDSEC EQU X'01' MARIST SECURE OPTION IS ON SECQNAM
+
+FILE: HCPVMDBK SECQNAM E1
+./ R 23620002 $ 23621000 100 05/13/94 10:37:17
+*MDUSER3 DS F RESERVED FOR INSTALLATION USE SECQNAM 22340001
+VMDMARFL DS X MARIST FLAGS SECQNAM
+VMDSECUR EQU X'01' INDICATE SECURE USER SECQNAM
+VMDUSER3 DS 3X PLACE HOLDER FOR REST OF SPACE SECQNAM
+
+FILE: HCPLGN SECQNAM E1
+./ R 07730001 $ 7730100 100 02/14/92 11:16:31
+ BNO LGNSETSC BRANCH IF NOT SECQNAM 08048300
+./ I 07740001 $ 7740100 100 02/14/92 11:16:31
+LGNSETSC DS 0H SECQNAM 08048890
+ TM DVMDMARS,DVMDSEC IS SECURE OPTION SPECIFIED?SECQNAM 08048900
+ BNO LGNSETOR Branch if not SECQNAM 08048910
+ OI VMDMARFL,VMDSECUR SET SECURE BIT IN VMDBK SECQNAM 08048920
+
+FILE: HCPCQU SECQNAM E1
+./ R 03760001 $ 3760100 100 09/25/90 17:10:15
+* DETERMINE IF THIS IS A PRIVILEDGED USER - CLASS A, C SECQNAM 02420890
+* OR E CAN SEE ALL USERS, REGARDLESS OF VMDSECUR FLAG SECQNAM 02421780
+ TM VMDPCLB0,CLASSA IS THIS A CLASS A USER? SECQNAM 02422670
+ BO SETSECR YES, SET SECURITY FLAG SECQNAM 02423560
+ TM VMDPCLB0,CLASSC IS THIS A CLASS C USER? SECQNAM 02424450
+ BO SETSECR YES, SET SECURITY FLAG SECQNAM 02425340
+ TM VMDPCLB1,CLASSO IS THIS A CLASS O USER? SECQNAM 02424450
+ BO SETSECR YES, SET SECURITY FLAG SECQNAM 02425340
+ TM VMDPCLB0,CLASSE IS THIS A CLASS E USER? SECQNAM 02426230
+ BNO DOQRY NO, THEN GO ON WITH IT SECQNAM 02427120
+SETSECR OI QNAMFLG,QSECUR SET SECURITY FLAG ON SECQNAM 02428010
+DOQRY L R10,VMDORIG-VMDBK(,R11) FIRST VMDBK SECQNAM 02428900
+./ R 04300001 $ 4300100 100 09/25/90 17:10:15
+ TM VMDMARFL,VMDSECUR IS THIS A SECURE ACCOUNT? SECQNAM 02546590
+ BNO DOITNOW NOPE, THEN DISPLAY IT SECQNAM 02546680
+ TM QNAMFLG,QSECUR ARE WE PRIVILEDGED? SECQNAM 02546770
+ BNO DONXT THEN SKIP IT. SECQNAM 02546860
+DOITNOW ICM R8,B'1111',VMDRTERM IS THERE AN RDEV? SECQNAM 02546950
+./ R 16298001 $ 16298100 100 09/25/90 17:10:15
+QSNA EQU X'12' PRINT ONLY SNA USERS SECQNAM 10478190
+QSECUR EQU X'80' QUERY SECURE USERS SECQNAM 10478380
+
+******************************************************************************
+
+Set FROM command:
+
+ Modules affected: HCPFRM, HCPMSG, HCPQUY, HCPSET, HCPSFV,
+ HCPLDL, HCPMDLAT MACRO, HCPVMDBK COPY
+ This mod creates the SET and QUERY FROM commands. It creates
+ a field in the VMDBK from the VMDUSERx fields, which will hold
+ an 8 character text string. This string is created by the
+ SET FROM command and can be displayed using QUERY FROM. The
+ mod also changes spooling and message processing to make use
+ of this field when spooling a file to another vm or sending
+ a message. Essentially, this mod allows a privileged user to
+ "fake" their userid when sending a file, or message.
+
+ This mod was developed for VM/XA and the current version will
+ on VM/ESA 2.1.0. Martha McConaghy 6/24/96
+
+FILE: HCPSET FROMCMD E1
+./ I 04010001 $ 4010100 100 03/07/90 15:59:45
+************************************************************ FROMCMD 00396500
+* * FROMCMD 00396550
+* SET FROM COMMAND * FROMCMD 00396600
+* * FROMCMD 00396650
+************************************************************ FROMCMD 00396700
+ SPACE , FROMCMD 00396750
+ COMMD COMMAND=(FROM,4),EP=HCPFRMST,IBMCLASS=A, *00395450
+ CLASS=B FROMCMD 00396800
+ SPACE , FROMCMD 00396850
+ COMMD COMMAND=(FROM,4),EP=HCPFRMST,IBMCLASS=B, *00395450
+ CLASS=B FROMCMD 00396800
+ SPACE , FROMCMD 00396850
+ COMMD COMMAND=(FROM,4),EP=HCPFRMST,IBMCLASS=C, *00395450
+ CLASS=C FROMCMD 00396800
+ SPACE , FROMCMD 00396850
+
+FILE: HCPQUY FROMCMD E1
+./ I 06369991 $ 6370100 100 03/07/90 15:55:16
+************************************************************ FROMCMD 00524220
+* * FROMCMD 00524290
+* QUERY FROM COMMAND * FROMCMD 00524360
+* * FROMCMD 00524430
+************************************************************ FROMCMD 00524500
+ SPACE , FROMCMD 00524570
+ COMMD COMMAND=(FROM,4),EP=HCPFRMQU, *00524640
+ CLASS=A,IBMCLASS=A FROMCMD 00524710
+ COMMD COMMAND=(FROM,4),EP=HCPFRMQU, *00524780
+ CLASS=B,IBMCLASS=B FROMCMD 00524850
+ COMMD COMMAND=(FROM,4),EP=HCPFRMQU, *00524920
+ CLASS=C,IBMCLASS=C FROMCMD 00524990
+
+FILE: HCPMSU FROMCMD E1
+./ I 00845980 $ 846980 1000 05/08/94 15:35:24
+ COPY HCPVMDBK FROMCMD 00846980
+./ I 02370000 $ 2371000 1000 05/08/94 15:35:24
+* Marist Comments: FROMCMD
+* FROMCMD
+* 5/8/94: Modifications to support the FROM command have FROMCMD
+* been added by Martha McConaghy. This mod will FROMCMD
+* zap the userid portion of the message header FROMCMD
+* if a FROM value has been set using the SET FROMCMD
+* FROM command. FROMCMD
+* FROMCMD
+./ R 02480000 02560000 $ 2485990 5990 05/08/94 15:35:24
+ HCPUSING VMDBK,R11 FROMCMD 02486990
+ HCPUSING MSGPLIST,R1 FROMCMD 02493980
+ CLI VMDFROM,C' ' Check if From is on? FROMCMD 02500970
+ BE LEAVEIT No, then bye. FROMCMD 02507960
+ CLI VMDFROM,X'00' Check this one too.. FROMCMD 02514950
+ BE LEAVEIT No, bye. FROMCMD 02521940
+* SMSG and MSGNOH have no header field, so there is nothing FROMCMD
+* to zap. Therefore, exit if its either of those two types. FROMCMD
+ L R2,MSGTYPPT Now, check type of message FROMCMD 02528930
+ CLI 0(R2),MESSAGE Is it a message? FROMCMD 02535920
+ BE SETFROM Good, zap it. FROMCMD 02542910
+ CLI 0(R2),WARNING Is it a warning? FROMCMD 02549900
+ BE SETFROM Zap that too. FROMCMD 02556890
+./ R 02586990 $ 2590980 3990 05/08/94 15:35:24
+LEAVEIT SLR R15,R15 SET RETURN CODE OF 0 FROMCMD 02590980
+./ I 02603940 $ 2604040 100 05/08/94 15:35:24
+SETFROM DS 0H FROMCMD 02604040
+* Zap the "from" part of the message header with our FROMCMD
+* version of "from". FROMCMD
+ L R2,MSGTXTPT Get pointer to pointer FROMCMD 02604140
+ L R3,0(R2) Get pointer to actual text FROMCMD 02604240
+ MVC 12(8,R3),VMDFROM Overlay USERID with from txt FROMCMD 02604340
+ B LEAVEIT All done here FROMCMD 02604440
+./ R 02605920 $ 2606310 390 05/08/94 15:35:24
+ HCPDROP R1,R11,R13 FROMCMD 02606310
+./ I 02612850 $ 2612950 100 05/08/94 15:35:24
+* These equates map the message type flag in the Plist FROMCMD
+* pointed to by MSGTYPPT. See comments in beginning of FROMCMD
+* module for definitions. FROMCMD
+MESSAGE EQU X'80' FROMCMD 02612950
+MSGNOH EQU X'40' FROMCMD 02613050
+SMSG EQU X'20' FROMCMD 02613150
+WARNING EQU X'10' FROMCMD 02613250
+./ I 02620000 $ 2620900 900 05/08/94 15:35:24
+* The following DSECT maps the Plist passed to this module FROMCMD
+* in R1. The descriptions of each pointer in the Plist are FROMCMD
+* located at the beginning of this module. FROMCMD
+MSGPLIST DSECT FROMCMD 02621000
+MSGCTLPT DS 1F Ptr to delivery control flags FROMCMD 02622000
+MSGTYPPT DS 1F Ptr to message type flag and class FROMCMD 02623000
+MSGISSPT DS 1F Ptr to Issuer userid, reference only FROMCMD 02624000
+MSGLENPT DS 1F Ptr to length of message FROMCMD 02625000
+MSGTXTPT DS 1F Ptr to Ptr to message text FROMCMD 02626000
+MSGRECPT DS 1F Ptr to userid of message FROMCMD 02627000
+MSGHLNPT DS 1F Ptr to length of header FROMCMD 02628000
+
+FILE: HCPSFV FROMCMD E1
+./ I 04010000 $ 4010900 900 11/06/89 13:24:22
+* FROMCMD 04010900
+* TEST FOR VMDFROM FLAG - SET FROM USERID FROMCMD 04011800
+* FROMCMD 04012700
+ LTR R8,R8 IS THERE AN ALTERNATE USER? FROMCMD 04013600
+ BNZ NOFRMSET YES, THEN FORGET IT FROMCMD 04014500
+ CLI VMDFROM-VMDBK(R15),X'00' IS FROM SET? FROMCMD 04015400
+ BE NOFRMSET FROMCMD 04016300
+ CLI VMDFROM-VMDBK(R15),C' ' IS FROM SET? FROMCMD 04017200
+ BE NOFRMSET FROMCMD 04018100
+ MVC SPFORIG,VMDFROM-VMDBK(R15) FROMCMD 04019000
+./ R 04030000 $ 4034990 4990 11/06/89 13:24:22
+NOFRMSET LA R4,SPFOPUN ASSUME PUNCH IS CREATING FROMCMD 04034990
+
+FILE: HCPLDL FROMCMD E1
+./ * CO-REQ: HCPADR
+./ * IF-REQ: NONE
+./ * FORCE REASSEMBLY FOR HCPFRM MOD FOR HCPMDLAT MACRO
+
+FILE: HCPMDLAT FROMCMD E1
+./ I 66830002 $ 66830100 100 11/05/89 16:15:53
+ AIF ('&NAME'(1,6) NE 'HCPFRM' AND (NOT &HCPLLST) )*27692200
+ .EHCPFRM FROMCMD 27692400
+ HCPATTRB HCPFRM,MODATTR=(PAG,MP,DYN), *27692600
+ EP=((HCPFRMST,DYN),(HCPFRMQU,DYN)) FROMCMD 27692800
+ AIF ('&HCPATTRC' EQ '0').MDLATEX IF FOUND, EXIT FROMCMD 27693000
+.EHCPFRM ANOP , FROMCMD 27693200
+.**************************************************************FROMCMD 27693400
+
+FILE: HCPVMDBK FROMCMD E1
+./ R 23600002 23610002 $ 23611000 100 02/12/92 15:01:54
+*MDUSER1 DS F RESERVED FOR INSTALLATION USE FROMCMD 18114990
+*MDUSER2 DS F RESERVED FOR INSTALLATION USE FROMCMD 18119980
+VMDFROM DS 2F FROM FLAG WAS VMDUSER1 AND 2 FROMCMD 18124970
+
+FILE: HCPFRM ASSEMBLE E1
+FRM TITLE 'HCPFRM (CP) VM/XA SP 2' SPR3 00001000
+ ISEQ 73,80 VALIDATE SEQUENCING OF INPUT 00011000
+ COPY HCPOPTNS 00021000
+HCPFRM HCPPROLG ATTR=(PAGEABLE,REENTERABLE),BASE=(R12) 00031000
+*. 00041000
+* MODULE NAME - 00051000
+* 00061000
+* DMKFRM 00071000
+* 00081000
+* FUNCTION - 00091000
+* 00101000
+* TO PROCESS THE FOLLOWING USER-WRITTEN SET COMMAND 00111000
+* FUNCTIONS: 00121000
+* 00131000
+* SET FROM 00141000
+* QUERY FROM 00151000
+* 00161000
+* ATTRIBUTES - 00171000
+* 00181000
+* REENTRANT, PAGEABLE, CALLED VIA SVC 00191000
+* 00201000
+* ENTRY POINTS - 00211000
+* 00221000
+* HCPFRMST - PROCESS SET FROM COMMAND 00231000
+* HCPFRMQU - PROCESS QUERY FROM COMMAND 00241000
+* 00251000
+* ENTRY CONDITIONS - 00261000
+* 00271000
+* R0, R1, R2, R3 - WORK 00281000
+* R4, R5 - USED IN STACK 00291000
+* R6 - FUNCTION INDEX,VMDBK ANCHOR 00301000
+* R7 - QUERY PARM ADDR 00311000
+* R8 - QUERY PARM LENGTH 00321000
+* R11 - VMDBK ADDRESS 00331000
+* R12 - ENTRY POINT ADDRESS 00341000
+* R13 - SAVEAREA ADDRESS 00351000
+* 00361000
+* EXIT CONDITIONS - 00371000
+* 00381000
+* NORMAL - R2 = 0 00391000
+* ERROR - R2 = ERROR MESSAGE CODE NUMBER 00401000
+* 00411000
+* CALLS TO OTHER ROUTINES - 00421000
+* 00431000
+* HCPSCCFD - FIND NEXT ENTRY IN INPUT BUFFER 00451000
+* 00461000
+* EXTERNAL REFERENCES - 00471000
+* 00481000
+* TABLES/WORKAREAS - 00491000
+* 00501000
+* SAVBK WORK AREAS USED FOR SCRATCH DATA 00511000
+* VMDBK The VMDBK for the caller. CONSOLE 00512000
+* CONSOLE 00513000
+* MACROS - CONSOLE 00514000
+* CONSOLE 00515000
+* HCPCONSL WRITE TO USER'S TERMINAL CONSOLE 00516000
+* 00521000
+* REGISTER USAGE - 00531000
+* 00541000
+* R0 - Work CONSOLE 00556990
+* R1 - Work CONSOLE 00562980
+* R2 - PARAMETER PASSING 00571000
+* R3 - Length of message CONSOLE 00586990
+* R4 - Address of message block CONSOLE 00592980
+* R5-R10 - Work CONSOLE 00598970
+* R11 - VMDBLOK ADDRESS CONSOLE 00604960
+* R12 - BASE REGISTER 00611000
+* R13 - SAVEAREA ADDRESS 00621000
+* R14 - LINKAGE REGISTER 00631000
+* R15 - LINKAGE REGISTER 00641000
+* 00651000
+* 00661000
+* NOTES - 00671000
+* 00681000
+* THE OPERATION OF EACH SET FUNCTION IS DESCRIBED IN ITS 00691000
+* INDIVIDUAL PROLOGUE BELOW. 00701000
+* 00711000
+*. 00721000
+ EJECT 00731000
+ EXTRN HCPSCCFD 00751000
+ EJECT 00761000
+ PUNCH 'SPB' FORCE PAGE BOUNDARY ALIGNMENT 00771000
+ SPACE 3 00781000
+* HCPCSLPL is required for the HCPCONSL macro CONSOLE 00795990
+ COPY HCPCSLPL console parm list CONSOLE 00800980
+ COPY HCPEQUAT commonly used equates CONSOLE 00805970
+ COPY HCPPFXPG prefix page, needed by macros CONSOLE 00810960
+ COPY HCPSAVBK module save area block CONSOLE 00815950
+ COPY HCPVMDBK VM data blocks CONSOLE 00820940
+* 00831000
+ HCPUSING VMDBK,R11 00851000
+ HCPUSING PFXPG,R0 CONSOLE 00856000
+ HCPUSING SAVBK,R13 00861000
+ SPACE 3 00871000
+ EJECT 00881000
+*. 00891000
+* FUNCTION - 00901000
+* 00911000
+* SET FROM 00921000
+* 00931000
+* COMMAND FORMAT - 00941000
+* 00951000
+* CLASS A, B, OR C 00961000
+* 00971000
+* +-------+-----------------+ 00981000
+* | SET | FROM XXXXXXXX | 00991000
+* +-------+-----------------+ 01001000
+* 01011000
+* OPERATION - 01021000
+* 01031000
+FILE: HCPFRM ASSEMBLE E1 VM/ESA 3
+* 1. CALL DMKSCCFD TO LOCATE THE USERID. IF NO USERID 01041000
+* FOUND, BLANK OUT VMDFROM 01051000
+* 2. MOVE THE PARAMETER TO THE VMDBK. 01061000
+* 3. ISSUE THE 'COMMAND COMPLETE' MESSAGE AND EXIT. 01071000
+* 01081000
+* RESPONSES - 01091000
+* 01101000
+* COMMAND COMPLETE 01111000
+* 01121000
+* ERROR MESSAGES - 01131000
+* 01141000
+* DMKFRM002E INVALID OPERAND 01151000
+* 01161000
+*. 01171000
+ EJECT 01181000
+HCPFRMST HCPENTER CALL,SAVE=DYNAMIC FROM 01191000
+ MVC SETFROM,BLANKS Initialize save area CONSOLE 01207990
+ CALL HCPSCCFD Scan for FROM parameter CONSOLE 01214980
+ BNZ CLRFRM No parm? Then set to blanks CONSOLE 01221970
+ CL R0,EIGHT Is the parm length too long? CONSOLE 01228960
+ BH FRM002 Yes, then give error CONSOLE 01235950
+ LR R2,R0 Get length CONSOLE 01242940
+ BCTR R2,0 less 1 for execute CONSOLE 01249930
+ EX R2,FROMSAVE Store FROM parameter CONSOLE 01256920
+* CONSOLE 01263910
+* To get rid of previous FROM value, reset field CONSOLE 01270900
+* to blanks. Blanks are the same as no FROM being set. CONSOLE 01277890
+CLRFRM DS 0H 01291000
+ MVC VMDFROM(8),SETFROM Move FROM value to VMDBK CONSOLE 01310990
+ B FRMCOMSG All done, then leave. CONSOLE 01320980
+*. 01351000
+* FUNCTION - 01361000
+* 01371000
+* QUERY FROM 01381000
+* 01391000
+* COMMAND FORMAT - 01401000
+* 01411000
+* CLASS A, B, OR C 01421000
+* 01431000
+* +-------+--------+ 01441000
+* | QUERY | FROM | 01451000
+* +-------+--------+-----+ 01461000
+* | QUERY | FROM USERID | 01471000
+* +-------+--------------+ 01481000
+* 01491000
+* OPERATION - 01501000
+* 01511000
+* 1. PRINT THE MESSAGE AND EXIT 01521000
+* 01531000
+* RESPONSES - 01541000
+* 01551000
+* COMMAND COMPLETE 01561000
+* 01571000
+* ERROR MESSAGES - 01581000
+* 01591000
+* 01601000
+*. 01611000
+ EJECT 01621000
+HCPFRMQU HCPENTER CALL,SAVE=DYNAMIC FROM 01631000
+QRYFROM DS 0H 01641000
+ CALL HCPSCCFD Are there more parameters? CONSOLE 01660990
+ BZ MASSQRY Yes, must be mass query CONSOLE 01670980
+ CLI VMDFROM,C' ' Is there a FROM set for us? CONSOLE 01680970
+ BE NEGRESP No, then say so. CONSOLE 01690960
+ CLI VMDFROM,X'00' Same check. CONSOLE 01700950
+ BE NEGRESP No, then say so too. CONSOLE 01710940
+* Build response for QUERY FROM command CONSOLE 01720930
+ MVC FROMC,FROM Move in "From:" CONSOLE 01730920
+ MVC FROMID,VMDFROM Move in From userid CONSOLE 01740910
+ HCPCONSL WRITE,DATA=(FROMC,LMSGDS) Write it and leave CONSOLE 01750900
+ B FRMCOMSG CONSOLE 01760890
+ SPACE 1 01871000
+* If user specified a parameter, search VMDBK chain for any maCONSOLE 01885990
+* that have that FROM value set. CONSOLE 01890980
+MASSQRY DS 0H CONSOLE 01895970
+ MVI QRYFLAG,MSGCLEAR Clear the flag before beginniCONSOLE 01900960
+ CL R0,EIGHT Is the parm length too long? CONSOLE 01905950
+ BH FRM002 Yes, then give error CONSOLE 01910940
+ LR R8,R0 Save the length CONSOLE 01915930
+ BCTR R8,0 less 1 for execute CONSOLE 01920920
+ LR R7,R1 Save parameter CONSOLE 01925910
+ L R10,VMDORIG Point to system vmdbk CONSOLE 01930900
+ L R10,VMDCYCLE-VMDBK(R10) POINT TO FIRST ACTIVE VNEWOUT 01941000
+ HCPDROP R11 01951000
+ HCPUSING VMDBK,R10 01961000
+ MVC USERC,USERTXT Move in "Userid:" CONSOLE 01980990
+ MVC FROMC,FROM Move in "From:" CONSOLE 01990980
+* Now loop through VMDBK chain looking for anyone who has CONSOLE 02000970
+* the specified FROM set, or whose USERID equals the FROM. CONSOLE 02010960
+QRYNXT DS 0H 02041000
+ TM VMDCFLAG,VMDLOGON Is vm logging on? CONSOLE 02058990
+ BO QRYCHKEN Yes, skip it. CONSOLE 02066980
+ EX R8,CLCUSR Does USERID match FROM parm? CONSOLE 02074970
+ BE QRYMSG Yes, then print it CONSOLE 02082960
+ EX R8,CLCFROM Does FROM field match parm? CONSOLE 02090950
+ BE QRYMSG Yes, then print it CONSOLE 02098940
+QRYCHKEN DS 0H 02111000
+ CL R10,VMDORIG-VMDBK(,R11) ARE WE AT THE START? NEWOUT 02121000
+ BE QRYDONE Yes, then finish CONSOLE 02140990
+ L R10,VMDCYCLE Otherwise, get next VMDBK CONSOLE 02150980
+ B QRYNXT Do it again. CONSOLE 02160970
+* CONSOLE 02170960
+* Send negative msg or "complete" depending on outcome CONSOLE 02180950
+QRYDONE DS 0H CONSOLE 02190940
+ TM QRYFLAG,MSGSENT Did we send a message? CONSOLE 02200930
+ BO FRMCOMSG Yes, then send "complete" CONSOLE 02210920
+NEGRESP DS 0H CONSOLE 02220910
+ HCPCONSL WRITE,DATA=(NOFROM) CONSOLE 02230900
+ B FRMEXIT GO HOME 02281000
+* CONSOLE 02300990
+* Generate full message for user CONSOLE 02310980
+QRYMSG DS 0H 02321000
+ OI QRYFLAG,MSGSENT Say that message was printed CONSOLE 02336990
+ MVC USERID,BLANKS Clear out fields CONSOLE 02342980
+ MVC FROMID,BLANKS CONSOLE 02348970
+ MVC USERID,VMDUSER Move in current USERID CONSOLE 02354960
+ MVC FROMID,VMDFROM Move in from field CONSOLE 02360950
+ HCPCONSL WRITE,DATA=(FROMC,LMSGD) CONSOLE 02366940
+ LTR R15,R15 Did msg go through? CONSOLE 02372930
+ BNZ QRYDONE No, then end it now. CONSOLE 02378920
+ B QRYCHKEN Msg was OK, get next VMDBK CONSOLE 02384910
+* CONSOLE 02390900
+* The follwing will issue an error message if specified operanCONSOLE 02396890
+* is invalid because its too long. Max. of 8 chars. allowed. CONSOLE 02402880
+FRM002 DS 0H CONSOLE 02408870
+ HCPCONSL WRITE,DATA=(TOOLONG) CONSOLE 02414860
+ LA R2,002 CONSOLE 02420850
+* CONSOLE 02426840
+* Print generic "complete" message when all done CONSOLE 02432830
+FRMCOMSG DS 0H 02451000
+ HCPCONSL WRITE,DATA=(COMPLETE) CONSOLE 02470990
+* CONSOLE 02480980
+* Get out at last..... CONSOLE 02490970
+FRMEXIT DS 0H 02511000
+ HCPEXIT EP=(HCPFRMST,HCPFRMQU),SETCC=NO 02521000
+ EJECT 02661000
+***************************************************************CONSOLE 02661500
+* Data areas for HCPFRM CONSOLE 02662000
+* CONSOLE 02662500
+* Code for execute instructions CONSOLE 02663000
+CLCUSR CLC VMDUSER(*-*),0(R7) EXECUTED CONSOLE 02663500
+CLCFROM CLC VMDFROM(*-*),0(R7) EXECUTED CONSOLE 02664000
+FROMSAVE MVC SETFROM(*-*),0(R1) CONSOLE 02664500
+* CONSOLE 02665000
+* Constants for messages, etc. CONSOLE 02665500
+TOOLONG DC C'Operand too long' CONSOLE 02666000
+COMPLETE DC C'Command Complete' CONSOLE 02666500
+NOFROM DC C'No FROM Set' CONSOLE 02667000
+USERTXT DC C' Userid: ' CONSOLE 02667500
+FROM DC C'From: ' CONSOLE 02668000
+BLANKS DC C' ' CONSOLE 02668500
+EIGHT DC F'00000008' CONSOLE 02669000
+ LTORG , 02671000
+* Map of necessary work storage CONSOLE 02682990
+SAVBK DSECT , CONSOLE 02684980
+ ORG SAVEWRK0 CONSOLE 02686970
+FROMC DS CL6 CONSTANT 'FROM: ' 02691000
+FROMID DS CL8 VMDFROM 02701000
+LMSGDS EQU *-FROMC SHORT MESSAGE CONSOLE 02720990
+USERC DS CL9 CONSTANT ' USER: ' CONSOLE 02730980
+USERID DS CL8 VMUSER 02741000
+LMSGD EQU *-FROMC CONSOLE 02754990
+QRYFLAG DS X CONSOLE 02758980
+MSGCLEAR EQU X'00' CONSOLE 02762970
+MSGSENT EQU X'80' CONSOLE 02766960
+SETFROM DS CL8 CONSOLE 02770950
+ HCPDROP R0,R10,R13 CONSOLE 02774940
+ HCPEPILG 02781000
+
+************************************************************************
+The Marist INFOFOX modification allows an installation to define a new command
+that will translate into a dial into a predetermined virtual machine beginning
+at a specified address. This essentially allows you to define a synonym
+for "DIAL xxx cuu". At Marist, this is used to implement our INFOFOX BBS,
+which resides on system, MUSICB. A user issues the INFOFOX command, which
+will dial the MUSICB machine, at the first available port address above
+the address configured in the mod.
+
+The effect, therefore, enables you to reserve ports on a specific virtual
+machine for a specific application.
+
+The original modification was developed at McGill University, by Anne-Marie
+Marcoux (MARIE@VM1.McGill.CA). It supported only one synonym definition
+and the starting address was hardcoded in the mod. While the basic
+design of the modification remains the same,
+Marist has extended the original by adding a table which allows you to define
+multiple synonyms, each with its own starting address. It also
+now supports the command, "DIAL INFOFOX" in addition to the standard
+"INFOFOX" format. This was made necessary by a modification that Marist
+developed for the logo screen, which automatically places the DIAL command
+into the command stream. It is also useful, however for users who are
+used to issuing "DIAL xxxxxx".
+
+The most important part of the mod is in HCPDIA. This adds a new entry point
+to HCPDIA, HCPDIAIN. This routine is described more fully in the comments
+at the entry point. Its purpose is to compare the specified command with
+a pre-configured table. This table contains the synonym definition, its
+true virtual machine name and the beginning port address. If the specified
+command matches a synonym in the table, it is modified to contain the
+true virtual machine name and beginning address. In addition, a flag is set
+indicating that this was a synonym command. Control is then returned to
+the main part of HCPDIA. HCPSCNVT is called to find the first available port
+on the true virtual machine. HCPSCNVT has been modified to recognize that
+a synonym command was issued. In this case, it ignores all ports until it
+reaches the configured beginning address. It then returns the first available
+port.
+
+Currently, the INFOFOX mod supports only one synonym per virtual machine.
+This is because HCPSCNVT does not stop searching until it reaches
+the last port on the virtual machine. Therefore, you can control the
+beginning address of the range, but not the ending address. This should
+not be difficult to add, however, and is planned for the future.
+
+In addition to HCPDIA and HCPSCN, HCPCOM is modified to define the
+new synonyms. These should point to HCPDIAIN as the entry point
+and should be available in the pre-logon state.
+
+Also included are optional modifications to HCPBVM and HCPMES. These
+are necessary if you also have the PFKDIAL modification installed.
+They change the definition of the PF keys and the messages in the
+pre-logon state. These were required in the original mod from McGill
+but are no longer required.
+
+The mod in this file is for VM/ESA 2.1.0. The original version of this
+mod was developed on VM/ESA ESA 1.0 SLC 9202.
+
+As usual, you may alter this modification as you wish. Any problems,
+comments and feedback (but no complaints) can be sent to Martha
+McConaghy, Marist College (URMM@VM.MARIST.EDU) (914) 575-3252.
+06/24/96
+
+FILE: HCPDIA INFOFOX E1
+./ I 04240001 $ 4242001 2000 02/16/96 17:50:04
+* The following indicates a normal dial. The flag will INFOFOX 04242001
+* be set on if we are dialing to a userid in our INFOTAB. INFOFOX 04244001
+* See comments at HCPDIAIN. INFOFOX 04246001
+ MVI WHICHCMD,OTHERCMD INDICATE NORMAL DIAL INFOFOX 04248001
+./ I 04280001 $ 4285001 5000 02/16/96 17:50:04
+MAINENTR DS 0H Join main code from DIAIN INFOFOX 04285001
+./ I 05460001 $ 5463001 3000 02/16/96 17:50:04
+ CLI WHICHCMD,INFOCMD Are we doing info cmd? INFOFOX
+ BE NOCHECK INFOFOX
+./ I 05560001 $ 5565001 5000 02/16/96 17:50:04
+NOCHECK DS 0H INFOFOX
+./ I 06010001 $ 6012001 2000 02/16/96 17:50:04
+ LR R15,R0 Ensure length in R15 INFOFOX 06012001
+ B INFOCHK Go check for synonyms INFOFOX 06014001
+* INFOFOX 06016001
+GETVMDBK DS 0H Rejoin HCPDIA in progress INFOFOX 06018001
+./ I 07440001 $ 7443001 3000 02/16/96 17:50:04
+ CLI WHICHCMD,INFOCMD Are we doing the info thing? INFOFOX 07443001
+ BE FINDVDEV Yes, then search for vdev INFOFOX 07446001
+./ I 08020001 $ 8023001 3000 02/16/96 17:50:04
+* Set info flag for HCPSCNVT. INFOFOX 08023001
+ ICM R0,B'1000',WHICHCMD Pass flag for type of dial INFOFOX 08026001
+./ I 16690001 $ 16690101 100 02/16/96 17:50:04
+*START OF SPECIFICATIONS***************************************INFOFOX 16690101
+* INFOFOX 16690201
+* Entry point name - HCPDIAIN INFOFOX 16690301
+* INFOFOX 16690401
+* Descriptive name - Dial processing for INFOFOX and related INFOFOX 16690501
+* commands. INFOFOX 16690601
+* INFOFOX 16690701
+* Developed by Martha McConaghy for Marist College 8/6/92 INFOFOX 16690801
+* for VM/ESA 1.0. INFOFOX 16690901
+* Based on a modification developed at McGill University. INFOFOX 16691001
+* Updated for VM/ESA 2.1 by Martha McConaghy 5/5/94. INFOFOX 16691101
+* INFOFOX 16691201
+* Function - This routine allows you to define commands in CP INFOFOX 16691301
+* that are pseudonyms for a dial function. For INFOFOX 16691401
+* example, INFOFOX, will really dial MUSICB using INFOFOX 16691501
+* a specific address range. The pseudonym commands INFOFOX 16691601
+* and the real DIALEE's name are kept in a table INFOFOX 16691701
+* along with the starting address. The table is INFOFOX 16691801
+* INFOTAB, located in this module. If you don't INFOFOX 16691901
+* care about the vaddr of the dial port, specify INFOFOX 16692001
+* x'0000' in the table. INFOFOX 16692101
+* INFOFOX 16692201
+* There are two ways of entering this routine: INFOFOX 16692301
+* entering the vanilla command, ie. INFOFOX. The INFOFOX 16692401
+* operation will enter this routine via HCPDIAIN. INFOFOX 16692501
+* The second INFOFOX 16692601
+* way is to issue DIAL xxxx, ie. DIAL INFOFOX. INFOFOX 16692701
+* In this case, operation will enter via HCPDIAL INFOFOX 16692801
+* and will be sent down to INFOCHK before going on. INFOFOX 16692901
+* INFOFOX 16693001
+* Prior to entering HCPSCNVT at label SCANLOOP, we INFOFOX 16693101
+* will set a flag in R0 that indicates if we are INFOFOX 16693201
+* processing an INFO command or not. If we are, INFOFOX 16693301
+* HCPSCNVT will check the vaddr of each port againstINFOFOX 16693401
+* the address defined in INFOTAB. HCPSCNVT will INFOFOX 16693501
+* reject all vaddrs below this address. INFOFOX 16693601
+* INFOFOX 16693701
+* R2, R3 and R4 are used. INFOFOX 16693801
+* R15 is expected to have the length of dialee's name INFOFOX 16693901
+* upon input. INFOFOX 16694001
+* INFOFOX 16694101
+*END OF SPECIFICATIONS*****************************************INFOFOX 16694201
+HCPDIAIN HCPENTER CALL,SAVE=DYNAMIC INFOFOX 16694301
+* INFOFOX 16694401
+* Enter here for one of the special commands and then join INFOFOX 16694501
+* the main HCPDIA for terminal checking. We will come back INFOFOX 16694601
+* later on. INFOFOX 16694701
+ MVI WHICHCMD,INFOCMD Signal the info command INFOFOX 16694801
+ B MAINENTR Rejoin HCPDIA INFOFOX 16694901
+* INFOFOX 16695001
+INFOCHK DS 0H Main checking code INFOFOX 16695101
+* INFOFOX 16695201
+* The following code will check a table of pseudonym INFOFOX 16695301
+* commands. If the userid matches one, then we will INFOFOX 16695401
+* will reset the DIALEUSR name to the table value and set the INFOFOX 16695501
+* vaddr value for HCPSCNVT in VNUMBIN. INFOFOX 16695601
+* INFOFOX 16695701
+* Restrictions: We expect R15 to have length in it. INFOFOX 16695801
+* Also, R1 should point to DIALEUSR. INFOFOX 16695901
+* INFOFOX 16696001
+* Set a minimum length for R15. This is to ensure that there INFOFOX 16696101
+* is at least 3 characters in the command to be checked. INFOFOX 16696201
+* Otherwise, send it back to HCPDIA to be handled normally. INFOFOX 16696301
+ BCTR R15,0 Got to sub 1 from length INFOFOX 16696401
+ CL R15,PFX2 Is length < 3? INFOFOX 16696501
+ BL GETVMDBK Yes, then skip check. INFOFOX 16696601
+* INFOFOX 16696701
+ LA R2,20 Set Table Increment INFOFOX 16696801
+ LA R3,EINFOTAB Set ending address INFOFOX 16696901
+ LA R4,INFOTAB Get addr of our table INFOFOX 16697001
+COMPTAB EX R15,COMPINFO Is it an entry in the table? INFOFOX 16697101
+ BE INFOSET yes, then set the information INFOFOX 16697201
+ BXLE R4,R2,COMPTAB Get next entry and try again INFOFOX 16697301
+ B GETVMDBK None found, go back to HCPDIA. INFOFOX 16697401
+* INFOFOX 16697501
+INFOSET MVI WHICHCMD,INFOCMD Indicate the info command INFOFOX 16697601
+ MVC DIALEUSR(8),8(R4) Set dialee name from table INFOFOX 16697701
+ LA R0,8 Hardcode length at 8 INFOFOX 16697801
+ MVC VNUMSTRT,16(R4) Set Vaddr for HCPSCNVT INFOFOX 16697901
+ B GETVMDBK Rejoin HCPDIA INFOFOX 16698001
+ EJECT INFOFOX 16698101
+./ I 27320971 $ 27321671 700 02/16/96 17:50:04
+COMPINFO CLC DIALEUSR(*-*),0(R4) CHECK DIALED VM INFOFOX 27321671
+* INFOFOX 27322371
+INFOTAB DC CL16'INFOFOX MUSICB ' SYNONYM AND SYSTEM INFOFOX 27323071
+ DC X'000005E0' BEGIN VADDR INFOFOX 27323771
+ DC CL16'DOBIS MVS ' SYNONYM AND SYSTEM INFOFOX 27324471
+ DC X'00000576' BEGIN VADDR INFOFOX 27325171
+ DC CL16'TSO MVS ' SYNONYM AND SYSTEM INFOFOX 27325871
+ DC X'00000000' BEGIN VADDR INFOFOX 27326571
+ DC CL16'IAPROD MVS ' SYNONYM AND SYSTEM INFOFOX 27327271
+ DC X'00000000' BEGIN VADDR INFOFOX 27327971
+EINFOTAB EQU *-1 INFOFOX 27328671
+./ I 28520001 $ 28522001 2000 02/16/96 17:50:04
+ ORG SAVEWRK9 INFOFOX 28522001
+WHICHCMD DS XL1 is it INFO or normal DIAL ? INFOFOX 28524001
+OTHERCMD EQU X'00' INFOFOX 28526001
+INFOCMD EQU X'01' INFOFOX 28528001
+
+FILE: HCPMES INFOFOX E1
+./ R 61255400 61255450 $ 61255410 2 08/11/92 09:51:19
+70600506 PF3 or PF15: LOGOFF INFOFOX 74808974
+70600507 PF4 or PF16: INFOFOX INFOFOX 74808976
+70600508 PF5 or PF17: DOBIS INFOFOX 74808978
+70600509 PF6 or PF18: IAPROD INFOFOX 74808980
+70600510 PF12 or PF24: ADDR INFOFOX 74808982
+70600511 INFOFOX 74808982
+70600512 To access CMS, type: LOGON accountcode INFOFOX 74808982
+./ R 61255900 61255950 $ 61255910 2 08/11/92 09:51:19
+70600606 PF3: LOGOFF INFOFOX 74809054
+70600607 PF4: INFOFOX INFOFOX 74809056
+70600608 PF5: DOBIS INFOFOX 74809058
+70600609 PF6: IAPROD INFOFOX 74809060
+70600610 PF12: ADDR INFOFOX 74809062
+
+FILE: HCPCOM INFOFOX E1
+./ I 06344991 $ 6345000 100 08/06/92 15:11:26
+************************************************************** INFOFOX 00513910
+* * INFOFOX 00514900
+* DOBIS COMMAND * INFOFOX 00515890
+* * INFOFOX 00516880
+************************************************************** INFOFOX 00517870
+ SPACE , INFOFOX 00518860
+ COMMD COMMAND=(DOBIS,5),FL=CMDONLY+CMDALOG+CMDOLOG, *00519850
+ CLASS=*,EP=HCPDIAIN INFOFOX 00520840
+ SPACE , INFOFOX 00521830
+./ I 07869991 $ 7870000 100 08/06/92 15:11:26
+************************************************************** INFOFOX 00662410
+* * INFOFOX 00663400
+* IAPROD COMMAND * INFOFOX 00664390
+* * INFOFOX 00665380
+************************************************************** INFOFOX 00666370
+ SPACE , INFOFOX 00667360
+ COMMD COMMAND=(IAPROD,6),FL=CMDONLY+CMDALOG+CMDOLOG, *00668350
+ CLASS=*,EP=HCPDIAIN INFOFOX 00669340
+ SPACE , INFOFOX 00670330
+./ I 08064991 $ 8065000 100 08/06/92 15:11:26
+************************************************************** INFOFOX 00662410
+* * INFOFOX 00663400
+* INFOFOX COMMAND * INFOFOX 00664390
+* * INFOFOX 00665380
+************************************************************** INFOFOX 00666370
+ SPACE , INFOFOX 00667360
+ COMMD COMMAND=(INFOFOX,4),FL=CMDONLY+CMDALOG+CMDOLOG, *00668350
+ CLASS=*,EP=HCPDIAIN INFOFOX 00669340
+ SPACE , INFOFOX 00670330
+./ I 14379991 $ 14380000 100 08/06/92 15:11:26
+ SPACE , INFOFOX 00670330
+************************************************************** INFOFOX 00662410
+* * INFOFOX 00663400
+* TSO COMMAND * INFOFOX 00664390
+* * INFOFOX 00665380
+************************************************************** INFOFOX 00666370
+ SPACE , INFOFOX 00667360
+ COMMD COMMAND=(TSO,3),FL=CMDONLY+CMDALOG+CMDOLOG, *00668350
+ CLASS=*,EP=HCPDIAIN INFOFOX 00669340
+
+FILE: HCPBVM INFOFOX E1
+./ R 12714401 12714801 $ 12714901 50 08/06/92 15:00:32
+ DC AL1(3),X'01',CL13'LOGOFF' INFOFOX 07977200
+ DC AL1(4),X'01',CL13'INFOFOX' INFOFOX 07977200
+ DC AL1(5),X'01',CL13'DOBIS' INFOFOX 07977200
+ DC AL1(6),X'01',CL13'IAPROD' INFOFOX 07977800
+ DC AL1(12),X'01',CL13'ADDR' INFOFOX 07978400
+
+**********************************************************************
+The PFKDIAL modification allows you to define PF keys to be used in
+the pre-logon state. These PF keys can be assigned CP commands that
+are also valid in the pre-logon state to make them easier to use.
+
+The original PFKDIAL mod came from Anne-Marie Marcoux of McGill
+University (MARIE@VM1.McGill.CA). The mod in this file is essentially
+the same as the original. A few serial numbers have been updated
+to conform with my level of VM/ESA 2.1.0 and support for
+other CP commands, besides DIAL, has been added. These changes are
+slight, however, and the mod remains essentially the McGill version.
+
+The bulk of the work is done by HCPBVM, which processes the PF key
+and contains a table of the key definitions. The original mod
+only allowed DIAL commands to be defined to each key, ie. it hardcoded
+DIAL into the processing of the key. I have extended the mod to allow
+any valid pre-logon CP command to be placed in the table.
+
+HCPLOG and HCPLON are modified to allow PF keys to be used. HCPMES
+and HCPMXRBK are modified to change the messages on the pre-logon
+screen.
+
+My thanks to Anne-Marie for sharing her mod with me and her
+permission to post in on the LISTSERV.
+
+As usual, you are free to do with this what you will. Questions,
+comments and feedback (but no complaints) can be sent to Martha
+McConaghy, Marist College (URMM@VM.MARIST.EDU) (914) 575-3252.
+06/24/96
+
+./ * HCPMES PFKDIAL
+./ * let users dial by hitting PF keys
+./ R 61254804 61414804 $ 61255000 50 07/10/92 10:40:48
+* PFKDIAL 74808908
+* Translation information for message 7060-05 PFKDIAL 74808916
+* No fields need to be translated PFKDIAL 74808924
+70600501R PFKDIAL 74808932
+70600502 Use a PFkey to access the desired system: PFKDIAL 74808940
+70600503 PFKDIAL 74808948
+70600504 PF1 or PF13: Dial MUSICA PFKDIAL 74808956
+70600505 PF2 or PF14: Dial MUSICB PFKDIAL 74808964
+70600506 PF11 or PF23: ADDR PFKDIAL 74808972
+70600507 PF12 or PF24: LOGOFF PFKDIAL 74808980
+* PFKDIAL 74808988
+* Translation information for message 7060-06 PFKDIAL 74808996
+* No fields need to be translated PFKDIAL 74809004
+70600601R PFKDIAL 74809012
+70600602 Use a PFkey to access the desired system: PFKDIAL 74809020
+70600603 PFKDIAL 74809028
+70600604 PF1: Dial MUSICA PFKDIAL 74809036
+70600605 PF2: Dial MUSICB PFKDIAL 74809044
+70600606 PF11: ADDR PFKDIAL 74809052
+70600607 PF12: LOGOFF PFKDIAL 74809060
+* PFKDIAL 74809068
+* Translation information for message 7060-07 PFKDIAL 74809076
+* No fields need to be translated PFKDIAL 74809084
+70600701R PFKDIAL 74809092
+70600702 Use a PFkey to access the desired system: PFKDIAL 74809100
+70600703 PFKDIAL 74809108
+70600704 PF1 or PF13: musicf PFKDIAL 74809116
+* PFKDIAL 74809124
+* Translation information for message 7060-08 PFKDIAL 74809132
+* No fields need to be translated PFKDIAL 74809140
+70600801R PFKDIAL 74809148
+70600802 Use a PFkey to access the desired system: PFKDIAL 74809156
+70600803 PFKDIAL 74809164
+70600804 PF1: musicf PFKDIAL 74809172
+* PFKDIAL 74809180
+
+FILE: HCPBVM PFKDIAL E1
+./ * HCPBVM PFKDIAL
+./ * let users dial by hitting PF keys
+./ * vmdpfunc points to a 24 fullword list which in turn points
+./ * to gsdbloks for those pfkeys having settings
+./ * see module HCPPFK for pfk processing
+./ I 00935001 $ 937001 2000 05/03/94 17:53:42
+* HCPGSDBK - General System Data Block PFKDIAL
+./ I 00980001 $ 985001 5000 05/03/94 17:53:42
+* HCPPFUNC - PF Keys Function block PFKDIAL
+./ I 01965001 $ 1966000 1000 05/03/94 17:53:42
+ COPY HCPGSDBK - General system data block PFKDIAL 01962001
+./ I 02000001 $ 2005001 5000 05/03/94 17:53:42
+ COPY HCPPFUNC - PF Keys Function Table PFKDIAL 02005001
+./ I 10070001 $ 10070101 100 05/03/94 17:53:42
+************************************************************** PFKDIAL 10070101
+* * PFKDIAL 10070201
+* Now create PFK entries for menu to be shown at logon * PFKDIAL 10070301
+* Make sure R8 and R10 are unchanged * PFKDIAL 10070401
+* * PFKDIAL 10070501
+************************************************************** PFKDIAL 10070601
+ HCPUSING RDEV,R8 PFKDIAL 10070701
+PFKDIAL CLI RDEVCLAS,CLASGRAF is this a local 3270? PFKDIAL 10070801
+ BNE PFKDONE no, then no PFKs PFKDIAL 10070901
+PFKDEFS LA R2,PFDEFS R2 -> list of definitions PFKDIAL 10071001
+ HCPUSING SYSDEF,R2 PFKDIAL 10071101
+ LA R4,NPFDEFS R4 = number of these definitions PFKDIAL 10071201
+* just covering here, an already defined function table PFKDIAL 10071301
+* means it wasn't released properly last time around! PFKDIAL 10071401
+* and that's a BUG !!! PFKDIAL 10071501
+ ICM R1,B'1111',VMDPFUNC get the function table PFKDIAL 10071601
+ BNZ FILLKEYS this is not the first time PFKDIAL 10071701
+* In VM/ESA 2.1, the PFUNC block was added to map the list PFKDIAL
+* of pointers to the PF key definitions. We must use that PFKDIAL
+* block layout now. PFKDIAL
+ LA R0,PFUNSZD(R1) Let's get one then. PFKDIAL 10071801
+ HCPGETST LEN=(R0),TYPE=GUESTPERM get free storage PFKDIAL 10071901
+ ST R1,VMDPFUNC save that precious address PFKDIAL 10072001
+ LR R6,R1 PFKDIAL 10072101
+ HCPUSING PFUNC,R6 PFKDIAL 10072201
+ XC 0(PFUNSZD*8,R1),0(R1) Zero out the whole thing PFKDIAL 10072501
+ LA R6,PFUNGSDS Get location of PF pointers PFKDIAL 10072601
+ HCPDROP R6 PFKDIAL 10072701
+* Now load the PFUNC table and create GSD's for each PFKDIAL
+* PF key to be defined. PFKDIAL
+FILLKEYS SLR R3,R3 clear to PFKDIAL 10072801
+ IC R3,SYSKEY ... load PF key number PFKDIAL 10072901
+ LA R0,PFDENTRY size of block PFKDIAL 10073001
+ HCPGETST ID=GSDBK,LEN=(R0),TYPE=GUESTPERM get it! PFKDIAL 10073101
+ SLL R3,2 Calculate the index into the PFKDIAL 10073201
+ S R3,PFX4 ...function table for this PF PFKDIAL 10073301
+ AR R3,R6 Add offset to table pointer PFKDIAL 10073401
+ ST R1,0(,R3) store gsdbk into addr table slot PFKDIAL 10073501
+ SPACE 1 PFKDIAL 10073601
+ HCPUSING GSDBK,R1 addressability to PF GSDBK PFKDIAL 10073701
+ STH R0,GSDFRESZ save the double word size PFKDIAL 10073801
+ LA R5,L'SYSNAME length of data to be moved PFKDIAL 10073901
+ STH R5,GSDDCNT save the pfdata data count PFKDIAL 10074001
+ MVC GSDDATA(L'SYSNAME),SYSNAME move part 2 PFKDIAL 10074201
+ MVC GSDFLAG,=AL1(GSDPFIMM) immediate PF key PFKDIAL 10074301
+ CLI RDEVTYPE,TYP3278 got 24-PFkey terminal ? PFKDIAL 10074401
+ BNE NEXTSYS nope, all done for this one then PFKDIAL 10074501
+ SPACE 1 PFKDIAL 10074601
+ HCPGETST ID=GSDBK,LEN=(R0),TYPE=GUESTPERM get one morPFKDIAL 10074701
+ LA R3,4*12(,R3) repeat at PF(n+12) PFKDIAL 10074801
+ ST R1,0(,R3) store gsdbk into addr table slot PFKDIAL 10074901
+ HCPUSING GSDBK,R1 addressability to PF GSDBK PFKDIAL 10075001
+ STH R0,GSDFRESZ save the double word size PFKDIAL 10075101
+ STH R5,GSDDCNT save the pfdata data count PFKDIAL 10075201
+* MVC GSDDATA(L'DIAL),DIAL move pfdata part 1 PFKDIAL 10075301
+ MVC GSDDATA(L'SYSNAME),SYSNAME move part 2 PFKDIAL 10075401
+ MVC GSDFLAG,=AL1(GSDPFIMM) immediate PF key PFKDIAL 10075501
+ SPACE 1 PFKDIAL 10075601
+NEXTSYS LA R2,SYSLEN(,R2) PFKDIAL 10075701
+ BCT R4,FILLKEYS move on to next definition PFKDIAL 10075801
+ HCPDROP R1,R2,R8 PFKDIAL 10075901
+ SPACE 1 PFKDIAL 10076001
+PFKDONE DS 0H PFKDIAL 10076101
+./ I 12710001 $ 12710401 400 05/03/94 17:53:42
+ EJECT PFKDIAL 12710301
+* PFdefs : PFKDIAL 12711201
+* Byte 1 = PFK number PFKDIAL 12711501
+* Byte 2 = cpu number X'01' --> VM1 PFKDIAL 12711801
+* X'02' --> VM2 PFKDIAL 12712101
+* X'00' --> system runs on both PFKDIAL 12712401
+* BYTE 3-15 = command to issue PFKDIAL 12712701
+* PFKDIAL 12713001
+PFDEFS DC AL1(1),X'01',CL13'DIAL MUSICA' PFKDIAL 12713301
+ DC AL1(2),X'01',CL13'DIAL MUSICB' PFKDIAL 12713601
+ DC AL1(11),X'01',CL13'ADDR' PFKDIAL 12713901
+ DC AL1(12),X'01',CL13'LOGOFF' PFKDIAL 12714201
+ SPACE 1 PFKDIAL 12714501
+NPFDEFS EQU (*-PFDEFS)/15 PFKDIAL 12714801
+PFDENTRY EQU (L'SYSNAME+7)/8+GSDHSIZE double words PFKDIAL 12715101
+* Size of each PFkey entry PFKDIAL 12715401
+ SPACE 1 PFKDIAL 12715701
+SYSDEF DSECT initial PFK definition PFKDIAL 12716001
+SYSKEY DS X PFK number PFKDIAL 12716301
+SYSFLAG DS X cpu flag PFKDIAL 12716601
+SYSNAME DS CL13 name of system to dial PFKDIAL 12716901
+SYSLEN EQU *-SYSDEF length of this dsect PFKDIAL 12717201
+ SPACE 3 PFKDIAL 12717501
+
+FILE: HCPLOG PFKDIAL E1
+./ * HCPLOG PFKDIAL
+./ * let users dial by hitting PFkeys
+./ I 00970001 $ 00970100
+* HCPPFKPG - Deallocate PF key storage PFKDIAL
+./ I 02820001 $ 02820100
+ EXTRN HCPPFKPG PFKDIAL
+./ * this part entered if new user is logging on
+./ * skeleton vmblok must reset pfkeys first
+./ I 11090001 $ 11090100 100
+ HCPCALL HCPPFKPG deallocate PF key storage PFKDIAL
+ SPACE 1 PFKDIAL
+
+FILE: HCPLON PFKDIAL E1
+./ * HCPLON PFKDIAL
+./ * let users dial by hitting PF keys
+./ R 10030001 $ 10034991 4990 05/02/94 15:53:00
+ HCPDROP R3 PFKDIAL 10030005
+./ R 10060001 $ 10060991 990 05/02/94 15:53:00
+ LA R14,5 Hardcode version 5 PFKDIAL
+ CLI RDEVTYPE,TYP3278 Do we have 24 PF keys? PFKDIAL
+ BE PTFOK Yes, then we are cool. PFKDIAL
+ HCPDROP R8 PFKDIAL
+ LA R14,6 No, then use version 6 PFKDIAL
+PTFOK BCTR R14,0 Decrement for offset PFKDIAL 10060001
\ No newline at end of file
diff --git a/vmworkshop-vmarcs/1996/msg/README.md b/vmworkshop-vmarcs/1996/msg/README.md
new file mode 100644
index 0000000..c8e1b69
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/README.md
@@ -0,0 +1,43 @@
+# MSGD
+
+This is freely distributable software. The author shall not be held
+liable for any incorrect operation of your system(s) resulting from
+using this package. (having said that, I think you'll find that it
+*does* work)
+
+MSGD is a server to handle the Message Send Protocol per RFC 1312.
+Neither the server nor the client implement all features of RFC 1312.
+Note that you can run the client without the server and vice versa.
+
+Files in the package are:
+MSGD EXEC
+TELL EXEC
+MSGD README (this file)
+MSGD DIRECT
+
+To run MSGD, you need:
+
+1. IBM VM TCP/IP Version 2 (5735-FAL)
+2. REXX/Sockets from Arty Ecock of CUNY
+3. a service virtual machine named MSGD
+
+MSGD is the TCP and UDP operative Message Send Protocol server. It needs port 18 from your TCP/IP service machine and likes to have class B (for MSGNOH), but will work with just class G (for MSG).
+
+TELL EXEC is a (from scratch) replacement for standard CMS TELL.
+
+The Message Send Protocol is considered experimental. Discussion and suggestions for improvement are requested by the authors of RFC 1312. This protocol is used to send a short message to a given user on a given host. Such message service is known in the VM world as "TELL". On VM, RSCS provides this kind of interactive messaging, but TCP/IP, until now, has not.
+
+The details of the protocol are discussed in RFC1312 TXT (included). Thanks go to Russell Nelson of Crynwr Software and Geoff Arnold of Sun Microsystems, Inc. for collaborating on this RFC.
+
+All you really need to do is:
+
+1. Create the MSGD virtual machine,
+2. put MSGD EXEC on a disk available to the MSGD service VM and arrange that MSGD EXEC is invoked (eg: from PROFILE),
+3. put the supplied TELL EXEC in such a place where your users can invoke it. (the supplied TELL EXEC should function as a direct replacement for the standard CMS TELL, but there is NO WARRANTY provided)
+
+## Bugs
+
+The client (TELL) is only TCP based, and should have UDP support for "broadcasts". (server handles both)
+
+The client ties-up CMS until the message is delivered. (just the sending virtual machine, not the whole system)
+
diff --git a/vmworkshop-vmarcs/1996/msg/getline.c b/vmworkshop-vmarcs/1996/msg/getline.c
new file mode 100644
index 0000000..83049b6
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/getline.c
@@ -0,0 +1,40 @@
+/* ------------------------------------------------------------- GETLINE
+ * Name: GETLINE/UFTXGETS/UFTXRCVS
+ * common Get/Receive String function
+ * Operation: Reads a CR/LF terminated string from socket s
+ * into buffer b. Returns the length of that string.
+ * Author: Rick Troth, Ithaca NY, Houston TX (METRO)
+ * Date: 1993-Sep-19, Oct-20
+ *
+ * See also: putline.c, netline.c
+ */
+int getline(s,b)
+ int s;
+ char *b;
+ {
+ char *p;
+ int i;
+
+ p = b;
+ while (1)
+ {
+ if (read(s,p,1) != 1) /* get a byte */
+ if (read(s,p,1) != 1) return -1; /* try again */
+ if (*p == '\n') break; /* NL terminates */
+ if (*p == 0x00) break; /* NULL terminates */
+/* if (*p == '\t') *p = ' '; ** [don't] eliminate TABs */
+ p++; /* increment pointer */
+ }
+ *p = 0x00; /* NULL terminate, even if NULL */
+
+ i = p - b; /* calculate the length */
+ if (i > 0 && b[i-1] == '\r') /* trailing CR? */
+ {
+ i = i - 1; /* shorten length by one */
+ p--; /* backspace */
+ *p = 0x00; /* remove trailing CR */
+ }
+
+ return i;
+ }
+
diff --git a/vmworkshop-vmarcs/1996/msg/hostname.exec b/vmworkshop-vmarcs/1996/msg/hostname.exec
new file mode 100644
index 0000000..6dc67f2
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/hostname.exec
@@ -0,0 +1,71 @@
+/* * Copyright 1995, Richard M. Troth, all rights reserved.
+ *
+ * Name: HOSTNAME EXEC
+ * display the TCP/IP hostname for this system
+ * Author: Rick Troth, Houston, Texas, USA
+ * Date: 1992-Sep-09, 1994-Aug-28, 1995-Aug-13, 31
+ *
+ * Note: Typical invokation is something like
+ *
+ * hostname -f for FQDN
+ * hostname -s for short hostname
+ * hostname -d for internet domain
+ */
+
+/* load the TCPIP DATA file, presuming it's available */
+Address COMMAND 'PIPE < TCPIP DATA | SPLIT BEFORE /;/' ,
+ '| NLOCATE /;/ | XLATE LOWER | STEM TCP.'
+If rc ^= 0 Then Exit rc
+
+Parse Source . type .
+
+Parse Arg args
+If type = "COMMAND" Then Say _hn(args)
+Else Return _hn(args)
+
+Exit
+
+
+/* ------------------------------------------------------------------ */
+_HN: Procedure Expose tcp.
+
+/* first find out what CP and CMS say */
+Address COMMAND 'PIPE CP QUERY USERID' ,
+ '| SPEC WORD 3 1 | XLATE LOWER | VAR CPH'
+Address COMMAND 'PIPE COMMAND IDENTIFY' ,
+ '| SPEC WORD 3 1 | XLATE LOWER | VAR CMSH'
+
+/* initialize strings */
+hostname = ""
+domainorigin = ""
+
+/* crunch the file into usable data */
+Do i = 1 To tcp.0
+ Parse Var tcp.i w1 w2 .
+ If Index(w1,':') > 0 Then Do
+ Parse Var tcp.i hh ':' w1 w2 .
+ If hh ^= cph & hh ^= cmsh Then Iterate
+ End /* If .. Do */
+ Select
+ When w1 = "hostname" Then hostname = w2
+ When w1 = "domainorigin" Then domainorigin = w2
+ Otherwise nop
+ End /* Else Do */
+ End /* Do For */
+
+/* build FQDN string from components */
+If hostname = "" Then hostname = cmsh
+If domainorigin ^= "" Then fqdn = hostname || '.' || domainorigin
+ Else fqdn = hostname
+/* we may be doing a little more work here and immediately above
+ but it simplifies the logic somewhat (and/or I'm just lazy */
+
+/* now decide just what to return */
+Parse Upper Arg flag . , .
+Select /* flag */
+ When flag = "-F" Then return fqdn
+ When flag = "-D" Then return domainorigin
+ When flag = "-S" Then return hostname
+ Otherwise return fqdn
+ End /* Select flag */
+
diff --git a/vmworkshop-vmarcs/1996/msg/msg.filelist b/vmworkshop-vmarcs/1996/msg/msg.filelist
new file mode 100644
index 0000000..c56a8f1
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/msg.filelist
@@ -0,0 +1,45 @@
+* © Copyright 1992, 1996, Richard M. Troth, all rights reserved.
+* (casita sourced)
+*
+* Name: MSG FILELIST
+* a list of all files associated with the
+* RFC 1312 (MSP) TELL/MSGD implementation for CMS
+* Author: Rick Troth, Houston, Texas, USA
+* Date: 1992-Jun-14, Dec-09, 1996-Feb-15
+*
+ MSG FILELIST * README "" 0
+*
+ MSGD EXEC *
+ MSGD README *
+ RFC1312 TXT *
+ MSGD DIRECT *
+*
+ TELL EXEC *
+ TELL REXX *
+ TELL HELPCMS *
+ USERLIST REXX *
+ HOSTNAME EXEC *
+*
+* MSGD is the TCP and UDP operative Message Send Protocol server.
+* It needs port 18 from your TCP/IP service machine and likes to have
+* class B (for MSGNOH), but will work with just class G (for MSG).
+*
+* TELL EXEC is a (from scratch) replacement for standard CMS TELL.
+*
+* Be sure to get the latest REXX/Sockets for this to work!
+*
+* UNIX client and server:
+ MSGC C *
+ MSGLOCAL C *
+ MSGCUFTD C *
+ MSGCMSP C *
+ MSGHNDLR H *
+ MSGCAT C *
+ MSGD C *
+ GETLINE C *
+ PUTLINE C *
+ USERID C *
+ TCPIO C *
+ MSG MAK *
+* MSGC C is TELL C for UNIX; see the makefile
+*
diff --git a/vmworkshop-vmarcs/1996/msg/msg.mak b/vmworkshop-vmarcs/1996/msg/msg.mak
new file mode 100644
index 0000000..317edf9
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/msg.mak
@@ -0,0 +1,40 @@
+#
+# Name: msg.mak (make file)
+# makefile for MSG package: tell, msgcat, msgd
+# Author: Rick Troth, Houston, Texas, USA
+# Date: 1996-Mar-25 (Oscar night), cut from main "makefile"
+#
+
+default: tell msgcat msgd
+
+msgcat: msg.mak msgcat.o putline.o
+ cc -o msgcat msgcat.o putline.o
+ strip msgcat
+
+msgd: msg.mak msgd.o putline.o tcpio.o
+ cc -o msgd msgd.o putline.o tcpio.o
+ strip msgd
+
+tell: msg.mak msgc.o msgcmsp.o msgcuftd.o msglocal.o \
+ getline.o tcpio.o userid.o putline.o
+ cc -o tell msgc.o msgcmsp.o msgcuftd.o msglocal.o \
+ getline.o tcpio.o userid.o putline.o
+ strip tell
+
+msgcmsp.o: msg.mak msghndlr.h msgcmsp.c
+ cc -c msgcmsp.c
+
+msgcuftd.o: msg.mak msghndlr.h msgcuftd.c
+ cc -c msgcuftd.c
+
+msglocal.o: msg.mak msghndlr.h msglocal.c
+ cc -c msglocal.c
+
+install: tell msgcat msgd
+ mv tell msgcat /usr/local/bin/.
+ mv msgd /usr/local/etc/.
+
+clean:
+ rm -f *.o *.a core a.out tell msgcat msgd
+
+
diff --git a/vmworkshop-vmarcs/1996/msg/msgc.c b/vmworkshop-vmarcs/1996/msg/msgc.c
new file mode 100644
index 0000000..95cc76c
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/msgc.c
@@ -0,0 +1,127 @@
+/* © Copyright 1994, 1996, Richard M. Troth, all rights reserved.
+ * (casita sourced)
+ *
+ * Name: msgc.c (tell.c)
+ * a multi-mode 'tell' command for UNIX
+ * Author: Rick Troth, Rice University, Houston, Texas, USA
+ * Date: 1994-Jul-25 and prior
+ */
+
+#include
+#include
+#include "msghndlr.h"
+
+/* ------------------------------------------------------------ MSGWRITE
+ * Try stock UNIX 'write' command if local user.
+ */
+int msgwrite(user,text)
+ char *user, *text;
+ {
+ char temp[256];
+ (void) sprintf(temp,"echo \"%s\" | write %s",text,user);
+ return system(temp);
+ }
+
+/* ------------------------------------------------------------ MSGSMTPS
+ * Try SMTP "send" command. (not always implemented)
+ */
+int msgsmtps(user,text)
+ char *user, *text;
+ {
+ return -1;
+ }
+
+/* ------------------------------------------------------------ MSGSMTPM
+ * Try SMTP mail. (advantage is direct -vs- queued)
+ */
+int msgsmtpm(user,text)
+ char *user, *text;
+ {
+ return -1;
+ }
+
+/* ------------------------------------------------------------- MSGMAIL
+ * Try queued mail (sendmail) as a last resort.
+ */
+int msgmail(user,text)
+ char *user, *text;
+ {
+ return -1;
+ }
+
+/* -------------------------------------------------------------- DOTELL
+ */
+int dotell(user,text)
+ char *user, *text;
+ {
+ if (msgcmsp(user,text) &&
+ msgcuftd(user,text) &&
+ msglocal(user,text) &&
+ msgwrite(user,text) &&
+ msgsmtps(user,text) &&
+ msgsmtpm(user,text) &&
+ msgmail(user,text)) return -1;
+ else return 0;
+ }
+
+/* ------------------------------------------------------------------ */
+int main(argc,argv)
+ int argc;
+ char *argv[];
+ {
+ int i, j, k;
+ char msgbuf[4096], *arg0;
+
+ arg0 = argv[0];
+
+ /* process options */
+ for (i = 1; i < argc && argv[i][0] == '-' &&
+ argv[i][1] != 0x00; i++)
+ {
+ switch (argv[i][1])
+ {
+ case 'v': (void) sprintf(msgbuf,
+ "%s: %s Internet TELL client",
+ arg0,MSG_VERSION);
+ (void) putline(2,msgbuf);
+ return 0;
+ break;
+ default: (void) sprintf(msgbuf,
+ "%s: invalid option %s",
+ arg0,argv[i]);
+ (void) putline(2,msgbuf);
+ return 20;
+ break;
+ }
+ }
+
+ /* confirm sufficient arguments */
+ if (argc < 2)
+ {
+ (void) system("xmitmsg -2 386");
+ return 24;
+ }
+
+ /* parse them */
+ if (argc > 2)
+ {
+ k = 0;
+ for (i = 2; i < argc; i++)
+ {
+ for (j = 0; argv[i][j] != 0x00; j++)
+ msgbuf[k++] = argv[i][j];
+ msgbuf[k++] = ' ';
+ }
+ msgbuf[k++] = 0x00;
+ (void) dotell(argv[1],msgbuf);
+ }
+ else while (1)
+ {
+ (void) getline(0,msgbuf);
+ if (msgbuf[0] == '.' && msgbuf[1] == 0x00) break;
+ (void) dotell(argv[1],msgbuf);
+ }
+ return 0;
+ }
+
+
diff --git a/vmworkshop-vmarcs/1996/msg/msgcat.c b/vmworkshop-vmarcs/1996/msg/msgcat.c
new file mode 100644
index 0000000..95b24f6
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/msgcat.c
@@ -0,0 +1,142 @@
+/* © Copyright 1995, Richard M. Troth, all rights reserved.
+ * (casita sourced)
+ *
+ * Name: msgcat.c
+ * writes incoming messages to standard output
+ * Author: Rick Troth, Houston, Texas, USA
+ * Date: 1995-Oct-26 and prior
+ *
+ * Operation: I think the original idea came from David Lippke,
+ * that there should be a FIFO in the home directory
+ * to which the user could attach any custom listener.
+ * This is a quick and easy solution using that technique.
+ */
+
+extern int errno;
+#include
+
+#include "msghndlr.h"
+
+/* -------------------------------------------------------------- ENVGET
+ * Returns a pointer to the value of the requested variable,
+ * or points to the end of the environment buffer.
+ */
+char *envget(env,var)
+ char *env, *var;
+ {
+ char *p, *q;
+
+ if (*env == 0x00) return env;
+
+ p = env; q = var;
+ while (*p)
+ {
+ while (*p == *q && *p && *q && *p != '=') { p++; q++; }
+ if (*p == '=' && *q == 0x00) return ++p;
+ while (*p++); q = var;
+ }
+
+ return p;
+ }
+
+/* ------------------------------------------------------------------ */
+main(argc,argv)
+ int argc; char *argv[];
+ {
+ int i, fd;
+ char buffer[4096], *p, *q, *arg0, *envbuf, *user;
+ char outbuf[4096];
+
+ arg0 = argv[0];
+
+ /* process options */
+ for (i = 1; i < argc && argv[i][0] == '-' &&
+ argv[i][1] != 0x00; i++)
+ {
+ switch (argv[i][1])
+ {
+ case 'v': (void) sprintf(buffer,
+ "%s: %s Internet TELL agent",
+ arg0,MSG_VERSION);
+ (void) putline(2,buffer);
+ return 0;
+ break;
+ case 'u': (void) close(1);
+ (void) open("/dev/console",O_WRONLY|O_NOCTTY,0);
+ (void) close(2);
+ user = argv[++i];
+ i = fork(); if (i < 1) return i;
+ break;
+ default: (void) sprintf(buffer,
+ "%s: invalid option %s",
+ arg0,argv[i]);
+ (void) putline(2,buffer);
+ return 20;
+ break;
+ }
+ }
+
+ /* close stdin */
+ (void) close(0);
+
+ /* loop forever */
+ while (1)
+ {
+ errno = 0;
+ sprintf(buffer,"%s/.msgpipe",getenv("HOME"));
+ fd = open(buffer,O_RDONLY);
+ if (fd < 0)
+ {
+ sprintf(buffer,"/tmp/%s.msgpipe",getenv("LOGNAME"));
+ fd = open(buffer,O_RDONLY);
+ }
+ if (fd < 0) break;
+
+ /* loop on message instance */
+ while (1)
+ {
+ i = read(fd,buffer,4096);
+ if (i < 1)
+ i = read(fd,buffer,4096);
+ if (i < 1) break;
+
+ /* be sure it's environment terminated (double NULL) */
+ buffer[i++] = 0x00; buffer[i++] = 0x00; buffer[i++] = 0x00;
+ /* and reference the environment */
+ envbuf = buffer; while (*envbuf) envbuf++; envbuf++;
+
+ /* remove trailing line breaks and white space */
+ i = strlen(buffer) - 1;
+ while (i >= 0 &&
+ (buffer[i] == '\n' || buffer[i] == '\r'
+ || buffer[i] == ' ')) buffer[i--] = 0x00;
+
+ /* remove CTRLs and canonicalize line breaks */
+ for (p = q = buffer; *p != 0x00; p++)
+ {
+ if (*p < '\r') *q++ = ' ';
+ if (*p < ' ' && *p != '\t') *q++ = '.';
+ else *q++ = *p;
+ }
+
+ /* now write the message text */
+ errno = 0;
+ sprintf(outbuf,"From %s(%s): %s",
+ envget(envbuf,"MSGHOST"),envget(envbuf,"MSGUSER"),buffer);
+/*
+ sprintf(outbuf,"From %s@%s: %s",
+ envget(envbuf,"MSGUSER"),envget(envbuf,"MSGHOST"),buffer);
+ */
+ if (putline(1,outbuf) < 0) break;
+ }
+ (void) close(fd);
+ }
+ (void) perror(buffer); /* argv[0]? */
+/*
+ (void) sprintf(buffer,"xmitmsg -2 -a errno %d",errno);
+ (void) system(buffer);
+ */
+ return 0;
+ }
+
+
diff --git a/vmworkshop-vmarcs/1996/msg/msgcmsp.c b/vmworkshop-vmarcs/1996/msg/msgcmsp.c
new file mode 100644
index 0000000..b11ad35
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/msgcmsp.c
@@ -0,0 +1,64 @@
+/* © Copyright 1994, 1996, Richard M. Troth, all rights reserved.
+ * (casita sourced)
+ *
+ * Name: msgcmsp.c
+ * part of multi-mode 'tell' command for UNIX
+ * Author: Rick Troth, Houston, Texas, USA
+ * Date: 1996-May-08 (split from tell.c)
+ */
+
+/* #include */
+#include
+#include "msghndlr.h"
+
+/* -------------------------------------------------------------- MSGMSP
+ * Try to send the message via MSP. See RFC 1312.
+ * This method should work even if the user is local,
+ * but requires an MSP server to be running (psbly under 'inetd').
+ */
+int msgcmsp(user,text)
+ char *user, *text;
+ {
+ char *host, temp[256], *p, *q, buffer[BUFSIZ];
+ int s, port;
+ extern char *userid();
+
+ /* parse */
+ host = user; user = p = temp;
+ while (*host != '@' && *host != 0x00) *p++ = *host++;
+ if (*host == '@') host++;
+ if (host == 0x0000 || *host == 0x00) host = MSP_HOST;
+ port = MSP_PORT;
+
+ /* try to contact the MSP server */
+ errno = 0;
+ (void) sprintf(temp,"%s:%d",host,port);
+ s = tcpopen(temp,0,0);
+ if (s < 0) return s;
+
+ /* build an MSP message structure */
+ p = buffer;
+ *p++ = 'B'; /* use "type B" MSP structure */
+ q = user; while (*q) *p++ = *q++; *p++ = 0x00;
+ q = "*"; while (*q) *p++ = *q++; *p++ = 0x00;
+ /* canonicalize text to CR/LF */
+ q = text; while (*q)
+ { if (*q == '\n') *p++ = '\r';
+ *p++ = *q++; } *p++ = 0x00;
+ q = userid(); while (*q) *p++ = *q++; *p++ = 0x00;
+ q = "?"; while (*q) *p++ = *q++; *p++ = 0x00; /* my terminal? */
+ q = "-"; while (*q) *p++ = *q++; *p++ = 0x00; /* ticket */
+ q = "-"; while (*q) *p++ = *q++; *p++ = 0x00; /* secure */
+ *p++ = 0x00;
+
+ /* send the message */
+ (void) write(s,buffer,p-buffer);
+ (void) read(s,buffer,BUFSIZ);
+
+ /* clean up */
+ (void) close(s);
+
+ return 0;
+ }
+
+
diff --git a/vmworkshop-vmarcs/1996/msg/msgcuftd.c b/vmworkshop-vmarcs/1996/msg/msgcuftd.c
new file mode 100644
index 0000000..83185a5
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/msgcuftd.c
@@ -0,0 +1,107 @@
+/* © Copyright 1994, 1996, Richard M. Troth, all rights reserved.
+ * (casita sourced)
+ *
+ * Name: msgcuftd.c
+ * part of multi-mode 'tell' command for UNIX
+ * (message client talking to UFT daemon)
+ * Author: Rick Troth, Rice University, Houston, Texas, USA
+ * Date: 1994-Jul-25 and prior, 1996-May-09 (split from tell.c)
+ */
+
+#include
+#include "msghndlr.h"
+
+/* ------------------------------------------------------------- MSGUFTD
+ * That's UFTD, not just UFT, because messaging isn't part of
+ * UFT protocol, but may be a feature of some UFTD servers.
+ */
+int msgcuftd(user,text)
+ char *user, *text;
+ {
+ char temp[256], ubuf[64], *host;
+ int port, rc, s;
+
+ /* parse */
+ host = user; user = ubuf;
+ while (*host != '@' && *host != 0x00) *user++ = *host++;
+ if (*host == '@') host++; *user = 0x00; user = ubuf;
+ if (host == 0x0000 || *host == 0x00) host = MSG_DEFAULT_HOST;
+ port = MSG_UFT_PORT;
+
+ /* try to contact the UFT server */
+ errno = 0;
+ (void) sprintf(temp,"%s:%d",host,port);
+ s = tcpopen(temp,0,0);
+ if (s < 0) return s;
+
+ /* wait on a UFT/1 or UFT/2 herald */
+ (void) tcpgets(s,temp,sizeof(temp));
+
+ /* now try a UFT "MSG" command, if available */
+ (void) sprintf(temp,"MSG %s %s",user,text);
+ (void) tcpputs(s,temp);
+
+ /* wait for ACK/NAK */
+ rc = uftcwack(s,temp,sizeof(temp));
+
+ /* say goodbye politely */
+ (void) tcpputs(s,"QUIT");
+ (void) uftcwack(s,temp,sizeof(temp));
+
+ /* return cleanly */
+ (void) close(s);
+ return rc;
+ }
+
+/* ------------------------------------------------------------ UFTCWACK
+ */
+int uftcwack(s,b,l)
+ int s; char *b; int l;
+ {
+ int i;
+ char *p;
+
+ while (1)
+ {
+ errno = 0;
+ i = tcpgets(s,b,l);
+ if (i < 0)
+ {
+ /* broken pipe or network error */
+ b[0] = 0x00;
+ return i;
+ }
+ switch (b[0])
+ {
+ case 0x00:
+ /* NULL ACK */
+ (void) strncpy(b,"2XX ACK (NULL)",l);
+ return 0;
+ case '6':
+ /* write to stdout, then loop */
+ p = b;
+ while (*p != ' ' && *p != 0x00) p++;
+ if (*p != 0x00) (void) putline(1,++p);
+ case '1': case '#': case '*':
+ /* discard and loop */
+ break;
+ case '2': case '3':
+ /* simple ACK or "more required" */
+ return 0;
+ case '4': case '5':
+ /* "4" means client is confused anyway,
+ and "5" means a hard error, so ... */
+ return -1;
+ default:
+ /* protocol error */
+ return -1;
+ }
+/*
+ if (uftcflag & UFT_VERBOSE)
+ if (b[0] != 0x00)
+ (void) putline(2,b);
+ */
+ }
+ }
+
+
diff --git a/vmworkshop-vmarcs/1996/msg/msgd.c b/vmworkshop-vmarcs/1996/msg/msgd.c
new file mode 100644
index 0000000..2915d29
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/msgd.c
@@ -0,0 +1,165 @@
+/* © Copyright 1995, Richard M. Troth, all rights reserved.
+ * (casita sourced)
+ *
+ * Name: msgd.c
+ * a simplistic MSP server using $HOME/.msgpipe FIFO
+ * Author: Rick Troth, Houston, Texas, USA
+ * Date: 1995-Oct-27 and prior
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "msghndlr.h"
+
+/* ------------------------------------------------------------------ */
+int main(argc,argv)
+ int argc;
+ char *argv[];
+ {
+ char buff[BUFSIZ], temp[BUFSIZ], idnt[BUFSIZ];
+ char *p, *user, *term, *text, *from, *ftty,
+ *time, *sqty, *host, *arg0;
+ int i, fd, msg_flag;
+ struct passwd *pwdent;
+ char pipe[256];
+
+ arg0 = argv[0];
+
+ msg_flag = MSG_IDENT;
+ /* process options */
+ for (i = 1; i < argc && argv[i][0] == '-' &&
+ argv[i][1] != 0x00; i++)
+ {
+ switch (argv[i][1])
+ {
+ case 'v': (void) sprintf(temp,
+ "%s: %s Internet TELL server",
+ arg0,MSG_VERSION);
+ (void) putline(2,temp);
+ return 0;
+ break;
+ case 'n': msg_flag = (msg_flag & ~MSG_IDENT);
+ break;
+ case 'i': msg_flag = (msg_flag | MSG_IDENT);
+ break;
+ default: (void) sprintf(temp,
+ "%s: invalid option %s",
+ arg0,argv[i]);
+ (void) putline(2,temp);
+ return 20;
+ break;
+ }
+ }
+
+ /* read the MSP data from the client */
+ read(0,buff,BUFSIZ);
+ p = buff;
+ user = term = text = "";
+ switch (*p++)
+ {
+ case 'A': user = p; while (*p) p++; p++;
+ term = p; while (*p) p++; p++;
+ text = p; while (*p) p++;
+ break;
+ case 'B': user = p; while (*p) p++; p++;
+ term = p; while (*p) p++; p++;
+ text = p; while (*p) p++; p++;
+ from = p; while (*p) p++; p++;
+ ftty = p; while (*p) p++; p++;
+ time = p; while (*p) p++; p++;
+ sqty = p; while (*p) p++;
+ break;
+ default: break;
+ }
+
+ /* maybe try an IDENT lookup? */
+ if (msg_flag & MSG_IDENT) (void) tcpident(0,idnt,sizeof(idnt));
+ host = idnt;
+ while (*host != 0x00 && *host != '@') host++;
+ if (*host == '@') *host++ = 0x00;
+ /* trust IDENT's user value (if available) over MSP's */
+ if (idnt[0] != 0x00) user = idnt;
+
+ /* clean-up the target username for security */
+ for (p = user; *p != 0x00 && *p >= ' ' &&
+ *p != '|' && *p != ';' &&
+ *p != '/' && *p != '*' &&
+ *p != '$' && *p != '\\'; p++); *p = 0x00;
+ if (*user == 0x00) user = "operator";
+
+ /* compute the message pipe path */
+ pwdent = getpwnam(user);
+ if (pwdent) sprintf(pipe,"%s/.msgpipe",pwdent->pw_dir);
+ else (void) sprintf(pipe,"/home/%s/.msgpipe",user);
+
+ /* try opening message pipe FIFO */
+ fd = open(pipe,O_WRONLY|O_NDELAY);
+ if (fd < 0 && errno == ENOENT)
+ {
+ (void) sprintf(pipe,"/tmp/%s.msgpipe",user);
+ fd = open(pipe,O_WRONLY|O_NDELAY);
+ }
+ if (fd < 0 && errno == ENOENT)
+ {
+ /* try creating the FIFO; world writable, yes! */
+ (void) mknod(pipe,S_IFIFO|0622,0);
+ }
+ if (fd < 0 && errno == ENXIO)
+ {
+ /* launch default message handler */
+ (void) sprintf(temp,"msgcat -u '%s'",user);
+ (void) system(temp);
+ /* see if that worked */
+ fd = open(pipe,O_WRONLY|O_NDELAY);
+ }
+ if (fd < 0)
+ {
+ /* everything failed; bail out */
+ sprintf(temp,"-%d (UNIX or POSIX ERRNO)",errno);
+ write(1,temp,strlen(temp)+1);
+ return fd;
+ }
+
+ /* build the buffer; begin at offset zero */
+ i = 0;
+
+ /* copy the message text */
+ p = text; while (*p) temp[i++] = *p++; temp[i++] = 0x00;
+
+ /* now environment variables; first, who from? */
+ p = "MSGFROM="; while (*p) temp[i++] = *p++;
+ p = from; while (*p) temp[i++] = *p++; temp[i++] = 0x00;
+
+ /* what type of message? (MSP if by way of this server) */
+ p = "MSGTYPE=MSP/"; while (*p) temp[i++] = *p++;
+ temp[i++] = buff[0]; temp[i++] = 0x00;
+
+ /* also ... who's it too? (in case that isn't obvious) */
+ p = "MSGUSER="; while (*p) temp[i++] = *p++;
+ p = user; while (*p) temp[i++] = *p++; temp[i++] = 0x00;
+
+ /* what about the sender's HOST address? */
+ p = "MSGHOST="; while (*p) temp[i++] = *p++;
+ p = host; while (*p) temp[i++] = *p++; temp[i++] = 0x00;
+
+ /* an additional NULL terminates the environment buffer */
+ temp[i++] = 0x00;
+
+ /* hand it off; feed the FIFO (hoping there's a listener) */
+ (void) write(fd,temp,i);
+
+ /* all done, so close the file descriptor */
+ (void) close(fd);
+
+ /* tell the client "okay" */
+ write(1,"+",1);
+
+ /* get outta here */
+ return 0;
+ }
+
+
diff --git a/vmworkshop-vmarcs/1996/msg/msgd.direct b/vmworkshop-vmarcs/1996/msg/msgd.direct
new file mode 100644
index 0000000..ac4001f
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/msgd.direct
@@ -0,0 +1,10 @@
+USER MSGD password 4M 12M GB 64 MSG00010
+* Note: MSGD will probably run fine in 2M or even less MSG00020
+ INCLUDE CMS MSG00030
+ ACIGROUP gggggggg MSG00040
+*:name.TCP/IP Message Send server :list.TCPMAINT MSG00050
+ ACCOUNT aaaaaaaa dddd MSG00060
+* Note: the PROFILE EXEC on TCPMAINT 591 calls MSGD for this SVM. MSG00070
+ LINK TCPMAINT 591 191 RR MSG00080
+ LINK TCPMAINT 592 192 RR MSG00090
+* MSG00100
diff --git a/vmworkshop-vmarcs/1996/msg/msgd.exec b/vmworkshop-vmarcs/1996/msg/msgd.exec
new file mode 100644
index 0000000..0291a5d
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/msgd.exec
@@ -0,0 +1,369 @@
+/* © Copyright 1995, Richard M. Troth, all rights reserved.
+ *
+ * Name: MSGD EXEC
+ * CMS MessageD TCP/IP (RFC 1312) message receiver
+ * Author: Rick Troth, Houston, Texas, USA
+ * Date: 1992-May-27, Jun-03, Jun-29, 1993-Feb-24
+ *
+ * Note: the one line that would enable the asterisk ('*')
+ * for "all users" is commented out, because most sites
+ * will probably *not* want such exposure.
+ *
+ * Changes:
+ * 1993-Feb-24: Converted to use REXX/Sockets (RXSOCKET version 2). RMT
+ * 1993-Aug-21: Present remote hostname in lower case. RMT
+ */
+
+Address "COMMAND"
+
+progid = "CMS MessageD 1.2.2"
+
+Parse Upper Arg port . '(' . ')' .
+
+If port = "" Then port = "18"
+
+If ^Datatype(port,'N') Then Do
+ Say "TCP/IP service port must be numeric."
+ Exit 24
+ End /* If .. Do */
+
+Say progid "starting"
+
+ stdin = 0
+ stdout = 1
+ stderr = 2
+
+/*
+ * Verify REXX/Sockets (RXSOCKET version 2).
+ */
+Parse Value Socket("VERSION") With rc . ver .
+If ver < 2 Then Do
+ Say progid "requires REXX/Sockets (RXSOCKET version 2)"
+ Exit -1
+ End /* If .. Do */
+
+/*
+ * Initialize REXX/Sockets.
+ */
+Parse Value Socket("INITIALIZE", "MessageD") With rc . maxdesc .
+If rc ^= 0 Then Do
+ Say tcperror()
+ Exit rc
+ End /* If .. Do */
+Say "REXX/Sockets initialized for" maxdesc "descriptors"
+
+/*
+ * Request a new socket descriptor (TCP protocol).
+ */
+Parse Value Socket("SOCKET", "AF_INET", "SOCK_STREAM") ,
+ With rc tcp
+If rc ^= 0 Then Do
+ Say tcperror()
+ Exit rc
+ End /* If .. Do */
+Say "TCP socket =" tcp
+
+/*
+ * Set this socket to non-blocking mode.
+ */
+Parse Value Socket("IOCTL", tcp, "FIONBIO", "ON") With rc .
+If rc ^= 0 Then Do
+ Say tcperror()
+ Exit rc
+ End /* If .. Do */
+
+/*
+ * Bind to port 18.
+ */
+Parse Value Socket("BIND", tcp, "AF_INET" port) With rc .
+If rc ^= 0 Then Do
+ Say tcperror()
+ Exit rc
+ End /* If .. Do */
+Say "Socket" tcp "bound to port" port
+
+/*
+ * Set the socket to listen for new connections.
+ */
+Parse Value Socket("LISTEN", tcp, maxdesc) With rc .
+If rc ^= 0 Then Do
+ Say tcperror()
+ Exit rc
+ End /* If .. Do */
+
+/*
+ * Request a new socket descriptor (UDP protocol).
+ */
+Parse Value Socket("SOCKET", "AF_INET", "SOCK_DGRAM") With rc udp .
+If rc ^= 0 Then Do
+ Say tcperror()
+ Exit rc
+ End /* If .. Do */
+Say "UDP socket =" udp
+
+/*
+ * Set this socket to non-blocking mode.
+ */
+Parse Value Socket("IOCTL", udp, "FIONBIO", "ON") With rc .
+If rc ^= 0 Then Do
+ Say tcperror()
+ Exit rc
+ End /* If .. Do */
+
+/*
+ * Bind to the right IP port.
+ */
+Parse Value Socket("BIND", udp, "AF_INET" port) With rc .
+If rc ^= 0 Then Do
+ Say tcperror()
+ Exit rc
+ End /* If .. Do */
+Say "Socket" udp "bound to port" port
+
+/*
+ * Enable ASCII <---> EBCDIC translation option.
+ */
+Parse Value Socket("SETSOCKOPT", udp, "SOL_SOCKET", "SO_ASCII", "ON") ,
+ With rc .
+If rc ^= 0 Then Do
+ Say tcperror()
+ Exit rc
+ End /* If .. Do */
+
+Say progid "waiting for a connection"
+
+Do Forever
+
+ Say "*" /* waiting */
+
+ Parse Value Socket("SELECT", "READ" tcp udp stdin) ,
+ With rc ec "READ" rl "WRITE" wl "EXCEPTION" xl
+ If rc ^= 0 Then Leave
+
+ If Find(rl,tcp) > 0 Then Call READ_TCP(tcp)
+ If Find(rl,udp) > 0 Then Call READ_UDP(udp)
+ If Find(rl,stdin) > 0 Then Leave
+
+ End /* Do Forever */
+
+/*
+ * Close primary TCP socket.
+ */
+Parse Value Socket("CLOSE", tcp) With rc .
+If rc ^= 0 Then Say tcperror()
+
+/*
+ * Close the UDP socket.
+ */
+Parse Value Socket("CLOSE", udp) With rc .
+If rc ^= 0 Then Say tcperror()
+
+/*
+ * Tell REXX/Sockets that we are done with this IUCV path.
+ */
+Parse Value Socket("TERMINATE") With rc .
+If rc ^= 0 Then Say tcperror()
+
+Exit
+
+
+
+/* ------------------------------------------------------------ READ_TCP
+ * Read packets from a TCP connection.
+ */
+READ_TCP: Procedure
+Parse Arg tcp
+
+/*
+ * Accept this inbound connection.
+ */
+Parse Value Socket("ACCEPT", tcp) With rc tcp client
+If rc ^= 0 Then Do
+ Say "READ_TCP:" tcperror()
+ Return
+ End /* If .. Do */
+Say "READ_TCP: accepted" tcp "at" Time()
+Say "READ_TCP: client" client
+
+/*
+ * Resolve remote hostname (if we can).
+ */
+Parse Var client . . cipa
+Parse Value Socket("RESOLVE", cipa) With rc RemoteAddress RemoteHost .
+If rc ^= 0 Then Do
+ Say "READ_TCP:" tcperror()
+ RemoteAddress = cipa
+ RemoteHost = cipa
+ End /* If .. Do */
+Else Do
+ Say "READ_TCP: Remote host looks like" RemoteHost
+ Say "READ_TCP: Accepted" tcp "from" RemoteHost "at" Time()
+ End /* Else Do */
+
+/*
+ * Enable ASCII <---> EBCDIC translation option.
+ */
+Parse Value Socket("SETSOCKOPT", tcp, "SOL_SOCKET", "SO_ASCII", "ON") ,
+ With rc .
+If rc ^= 0 Then Do
+ Say "READ_TCP:" tcperror()
+ Parse Value Socket("CLOSE", tcp) With .
+ Return
+ End /* If .. Do */
+
+/*
+ * Loop, reading message(s) from the client.
+ */
+Do Forever
+
+ /*
+ * Read a packet from the client.
+ */
+ Parse Value Socket("READ", tcp, 4096) With rc bc pack
+ If rc ^= 0 Then Do
+ Say "READ_TCP:" tcperror()
+ Parse Value Socket("CLOSE", tcp) With .
+ Return
+ End /* If .. Do */
+ If bc < 1 Then Leave
+
+ /*
+ * Process the MSP message packet.
+ */
+ Parse Var pack ver +1 .
+ Say "READ_TCP: Protocol version" ver
+ Select /* ver */
+ When ver = 'A' Then rs = reva(pack)
+ When ver = 'B' Then rs = revb(pack)
+ Otherwise rs = "unknown protocol version" ver
+ End /* Select ver */
+
+ /*
+ * Send some reply back to the client.
+ */
+ If rs = "" Then rs = '+' /* ACK */
+ Else Say "READ_TCP: Reply" rs
+ Parse Value Socket("WRITE", tcp, rs || '00'x) With rc bc .
+ If rc ^= 0 Then Do
+ Say "READ_TCP:" tcperror()
+ Parse Value Socket("CLOSE", tcp) With .
+ Return
+ End /* If .. Do */
+ If bc < 1 Then Leave
+
+ End /* Do Forever */
+
+/*
+ * All done, relinquish our socket descriptor.
+ */
+Parse Value Socket("CLOSE", tcp) With rc .
+If rc ^= 0 Then Do
+ Say "READ_TCP:" tcperror()
+ Return
+ End /* If .. Do */
+Say "READ_TCP: Closed" tcp "at" Time()
+
+Return
+
+
+
+/* ------------------------------------------------------------ READ_UDP
+ * Read packets as UDP datagrams.
+ */
+READ_UDP: Procedure Expose ticket.
+Parse Arg udp
+
+Parse Value Socket("RECVFROM", udp, 512) With rc afam sock cipa bc pack
+If rc ^= 0 Then Do
+ Say "READ_UDP:" tcperror()
+ Return
+ End /* If .. Do */
+If bc < 1 Then Return
+Say "READ_UDP: client" afam sock cipa
+
+Say "READ_UDP: client's IP address is" cipa
+Parse Value Socket("RESOLVE", cipa) With rc RemoteAddress RemoteHost .
+If rc ^= 0 Then Do
+ Say "READ_UDP:" tcperror()
+ Return
+ End /* If .. Do */
+Say "READ_UDP: remote host looks like" RemoteHost
+
+ Parse Var pack ver +1 .
+ Say "READ_UDP: Protocol version" ver
+ Select /* ver */
+ When ver = 'A' Then rs = reva(pack)
+ When ver = 'B' Then rs = revb(pack)
+ Otherwise rs = "unknown protocol version" ver
+ End /* Select ver */
+
+ If rs = "" Then rs = '00'x
+ Else Say "READ_UDP: reply" rs
+/* bc = Socket('SendTo', udp, rs, 0, client)
+ If bc = "-1" Then Do
+ Say "RXSOCKET subfunction SENDTO returned error" errno
+ Leave
+ End */
+
+Return
+
+
+
+/* ---------------------------------------------------------------- REVA
+ * Handle a Message Send Protocol "revision A" packet.
+ */
+REVA: Procedure Expose RemoteHost
+Parse Arg data
+'PIPE VAR REMOTEHOST | XLATE LOWER | VAR REMOTEHOST'
+
+Parse Var data ver +1 data
+Parse Var data user '00'x data; Upper user
+Parse Var data term '00'x data
+Parse Var data text '00'x data
+Parse Value Diagrc(08,'MSGNOH' user "From" ,
+ RemoteHost || ":" text) With 1 rc 10 . 17 rs '15'x .
+Say "REVA: leftover data:" data
+
+Return rs
+
+
+
+/* ---------------------------------------------------------------- REVB
+ * Handle a Message Send Protocol "revision B" packet.
+ */
+REVB: Procedure Expose RemoteHost ticket.
+Parse Arg data
+'PIPE VAR REMOTEHOST | XLATE LOWER | VAR REMOTEHOST'
+
+Parse Var data +1 data /* skip past protocol version byte */
+Parse Var data LocalUser '00'x data; Upper LocalUser
+Parse Var data LocalTerminal '00'x data /* not useable */
+Parse Var data MessageText '00'x data
+Parse Var data RemoteUser '00'x data
+Parse Var data RemoteTerminal '00'x data /* not used */
+Parse Var data ticket '00'x data
+Parse Var data secure '00'x data
+/* remainder of packet is ignored */
+
+If ticket.LocalUser = ticket Then Return '-' || "DUP_OF" ticket
+from = "From" RemoteHost || "(" || RemoteUser || "):"
+
+Parse Value Diagrc(08,'QUERY USER' LocalUser) With ,
+ 1 rc 10 . 17 rs '15'x .
+If rc ^= 0 Then Return '-' || rs
+/* If LocalUser = '*' Then LocalUser = "ALL" */
+
+Do While MessageText ^= ""
+ Parse Var MessageText text '25'x MessageText
+ Parse Var text text '0D'x .
+ Parse Value Diagrc(08,'MSGNOH' LocalUser from text) With ,
+ 1 rc 10 . 17 rs '15'x .
+ If rc = 1 | rc = -1 Then
+ Parse Value Diagrc(08,'MSG' LocalUser from text) With ,
+ 1 rc 10 . 17 rs '15'x .
+ If rc = 0 Then ticket.LocalUser = ticket
+ Else Return '-' || rs
+ End /* Do While */
+
+Return ""
+
diff --git a/vmworkshop-vmarcs/1996/msg/msghndlr.h b/vmworkshop-vmarcs/1996/msg/msghndlr.h
new file mode 100644
index 0000000..bf487f5
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/msghndlr.h
@@ -0,0 +1,29 @@
+/* © Copyright 1996, Richard M. Troth, all rights reserved.
+ * (casita sourced)
+ *
+ * Name: msghndlr.h
+ * header file for msgd.c and msgcat.c
+ * Author: Rick Troth, Houston, Texas, USA
+ * Date: 1994-Jul-26, 1996-Mar-24
+ */
+
+#define MSG_VERSION "MSG/1.3.0"
+
+#define MSG_IDENT 0x0001
+#define MSG_VERBOSE 0x0002
+
+#define MSP_HOST "localhost"
+#define MSG_DEFAULT_HOST "localhost"
+#define MSP_PORT 18
+#define MSG_MSP_PORT 18
+
+#define MSG_UFT_PORT 608
+
+#ifndef BUFSIZ
+#define BUFSIZ 4096
+#endif
+
+static char *msg_copyright =
+ "© Copyright 1996, Richard M. Troth, all rights reserved. ";
+
+
diff --git a/vmworkshop-vmarcs/1996/msg/msglocal.c b/vmworkshop-vmarcs/1996/msg/msglocal.c
new file mode 100644
index 0000000..77c6c3c
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/msglocal.c
@@ -0,0 +1,72 @@
+/* © Copyright 1994, 1996, Richard M. Troth, all rights reserved.
+ * (casita sourced)
+ *
+ * Name: msglocal.c
+ * a multi-mode 'tell' command for UNIX
+ * Author: Rick Troth, Houston, Texas, USA
+ * Date: 1994-Jul-25 and prior
+ */
+
+#include
+#include
+#include "msghndlr.h"
+
+/* ------------------------------------------------------------- HOMEDIR
+ * Attempt to write the message directly to the user's ".msgpipe".
+ */
+#include
+char *homedir(u)
+ char *u;
+ {
+ struct passwd *pwdent;
+ static char failsafe[256];
+ pwdent = getpwnam(u);
+ if (pwdent) return pwdent->pw_dir;
+ (void) sprintf(failsafe,"/home/%s",u);
+ return failsafe;
+ }
+
+/* ------------------------------------------------------------ MSGLOCAL
+ */
+int msglocal(user,text)
+ char *user, *text;
+ {
+ int fd;
+ char temp[256];
+
+ (void) sprintf(temp,"%s/.msgpipe",homedir(user));
+ fd = open(temp,O_WRONLY|O_NDELAY);
+/* if (fd < 0 && errno == ENOENT)
+ then 'mknod' with 622 perms (writable */
+ if (fd < 0 && errno == ENXIO)
+ {
+ /* launch our special application */
+ fd = open(temp,O_WRONLY|O_NDELAY);
+ }
+ if (fd < 0) return fd;
+
+ (void) write(fd,text,strlen(text));
+ (void) close(fd);
+
+ return 0;
+ }
+
+/*
+ O_NDELAY When opening a FIFO (named pipe - see
+ mknod(2V)) with O_RDONLY or O_WRONLY set:
+
+ If O_NDELAY is set:
+ An open() for reading-only returns
+ without delay. An open() for writing-
+ only returns an error if no process
+ currently has the file open for reading.
+
+ If O_NDELAY is clear:
+ A call to open() for reading-only blocks
+ until a process opens the file for writ-
+ ing. A call to open() for writing-only
+ blocks until a process opens the file
+ for reading.
+ */
+
+
diff --git a/vmworkshop-vmarcs/1996/msg/putline.c b/vmworkshop-vmarcs/1996/msg/putline.c
new file mode 100644
index 0000000..33be7ca
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/putline.c
@@ -0,0 +1,27 @@
+/* ------------------------------------------------------------- PUTLINE
+ * Name: PUTLINE/UFTXPUTS
+ * common Put String function
+ * Operation: Writes the NULL terminated string from buffer b
+ * to socket s with NL (UNIX text) line termination.
+ * Author: Rick Troth, Ithaca, NY / Houston, TX (METRO)
+ * Date: 1993-Sep-19, Oct-20
+ *
+ * See also: getline.c, netline.c
+ */
+int putline(s,b)
+ int s;
+ char *b;
+ {
+ int i, j;
+ char temp[4096];
+
+/* i = strlen(b); */
+ for (i = 0; b[i] != 0x00; i++) temp[i] = b[i];
+ temp[i] = '\n';
+ j = write(s,temp,i+1);
+/* b[i] = 0x00; */
+
+ if (j != i+1) return -1;
+ return i;
+ }
+
diff --git a/vmworkshop-vmarcs/1996/msg/tcpio.c b/vmworkshop-vmarcs/1996/msg/tcpio.c
new file mode 100644
index 0000000..cb6bd22
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/tcpio.c
@@ -0,0 +1,374 @@
+/* © Copyright 1995, Richard M. Troth, all rights reserved.
+ *
+ * Name: tcpiolib.c
+ * various TCP utility functions
+ * Author: Rick Troth, Houston, Texas, USA
+ * Date: 1995-Apr-19
+ */
+
+#include
+#include
+#include
+#include
+
+#define TCPSMALL 256
+#define TCPLARGE 4096
+
+int tcp_ubuf[TCPLARGE];
+int tcp_uoff, tcp_uend;
+char tcp_umsg[TCPSMALL];
+
+/* ------------------------------------------------------------- TCPOPEN
+ * Tries to mimick open(path,flags[,mode])
+ * but connects to a TCP port, not a local file.
+ */
+int tcpopen(host,flag,mode)
+ char *host; int flag; int mode;
+ {
+ int s, i, port, rc, j;
+ struct sockaddr name;
+ struct hostent *hent, myhent;
+ char *myhental[2], myhenta0[4], myhenta1[4];
+ char temp[TCPSMALL], *p, *q;
+
+ /* parse host address and port number by colon */
+ p = host; host = temp; i = 0;
+ while (i < TCPSMALL && *p != 0x00 && *p != ':')
+ host[i++] = *p++; host[i++] = 0x00;
+ if (*p != ':') port = 0;
+ else
+ {
+ p++; q = p;
+ while (i < TCPSMALL && *q != 0x00 && *q != ':')
+ temp[i++] = *q++; temp[i++] = 0x00;
+ port = atoi(p);
+ }
+
+ /* figure out where to connect */
+ hent = gethostbyname(host);
+ if (hent == NULL)
+ {
+ /* netDB lookup failed; numeric address supplied? */
+ p = host;
+ if (*p < '0' || '9' < *p) return -1;
+ hent = &myhent;
+ hent->h_addr_list = myhental; /* address list */
+ hent->h_addr_list[0] = myhenta0; /* address 0 */
+ hent->h_addr_list[1] = myhenta1; /* address 1 */
+ hent->h_addrtype = AF_INET;
+ hent->h_length = 4;
+
+ /* try to pick-apart the string as dotted decimal */
+ hent->h_addr_list[0][0] = atoi(p);
+ while (*p != '.' && *p != 0x00) p++; p++;
+ if (*p < '0' || '9' < *p) return -1;
+ hent->h_addr_list[0][1] = atoi(p);
+ while (*p != '.' && *p != 0x00) p++; p++;
+ if (*p < '0' || '9' < *p) return -1;
+ hent->h_addr_list[0][2] = atoi(p);
+ while (*p != '.' && *p != 0x00) p++; p++;
+ if (*p < '0' || '9' < *p) return -1;
+ hent->h_addr_list[0][3] = atoi(p);
+
+ /* dotted decimal worked! now terminate the list */
+ hent->h_addr_list[1][0] = 0; hent->h_addr_list[1][1] = 0;
+ hent->h_addr_list[1][2] = 0; hent->h_addr_list[1][3] = 0;
+ /* better form might be to use NULL pointer? */
+ hent->h_addr_list[1] = NULL;
+
+ /* and what else do we need to set? */
+ hent->h_name = host;
+ /* should probably call gethostbyaddr()
+ at this point; maybe in the next rev */
+ }
+
+ /* gimme a socket */
+ s = socket(AF_INET,SOCK_STREAM,0);
+ if (s < 0) return s;
+
+ /* build that structure */
+ name.sa_family = AF_INET;
+ name.sa_data[0] = (port >> 8) & 0xFF;
+ name.sa_data[1] = port & 0xFF;
+
+ /* try address one-by-one */
+ for (i = 0; hent->h_addr_list[i] != NULL; i++)
+ {
+ /* any more addresses? */
+ if (hent->h_addr_list[i] == NULL) break;
+ if (hent->h_addr_list[i][0] == 0x00) break;
+
+ /* fill-in this address to the structure */
+ for (j = 0; j < hent->h_length; j++)
+ name.sa_data[j+2] = hent->h_addr_list[i][j];
+ name.sa_data[j+2] = 0x00; /* terminate */
+
+ /* note this attempt */
+ (void) sprintf(tcp_umsg,"trying %d.%d.%d.%d\n",name.sa_data[2],
+ name.sa_data[3],name.sa_data[4],name.sa_data[5]);
+
+ /* can we talk? */
+ rc = connect(s, &name, 16);
+ if (rc == 0) return s;
+ }
+
+ /* can't seem to reach this host on this port :-( */
+ (void) close(s);
+ if (rc < 0) return rc;
+ return -1;
+ }
+
+/* ------------------------------------------------------------ TCPCLOSE
+ */
+int tcpclose(fd)
+ int fd;
+ {
+ return close(fd);
+ }
+
+/* ------------------------------------------------------------- TCPGETS
+ * Operation: Reads a CR/LF terminated string from socket s
+ * into buffer b. Returns the length of that string.
+ * Author: Rick Troth, Houston, Texas, USA
+ * Date: 1995-Apr-19
+ *
+ * See also: getline.c, putline.c, netline.c
+ */
+int tcpgets(s,b,l)
+ int s; char *b; int l;
+ {
+ char *p;
+ int i;
+
+ p = b;
+ for (i = 0; i < l; i++)
+ {
+ if (read(s,p,1) != 1) /* get a byte */
+ if (read(s,p,1) != 1) return -1; /* try again */
+ if (*p == '\n') break; /* NL terminates */
+ if (*p == 0x00) break; /* NULL terminates */
+/* if (*p == '\t') *p = ' '; ** [don't] eliminate TABs */
+ p++; /* increment pointer */
+ }
+ *p = 0x00; /* NULL terminate, even if NULL */
+
+ if (i > 0 && b[i-1] == '\r') /* trailing CR? */
+ {
+ i = i - 1; /* shorten length by one */
+ p--; /* backspace */
+ *p = 0x00; /* remove trailing CR */
+ }
+
+ tcp_uoff = 0;
+ tcp_uend = 0;
+ return i;
+ }
+
+/* ------------------------------------------------------------- TCPPUTS
+ * Operation: Writes the NULL terminated string from buffer b
+ * to socket s with CR/LF (network text) line termination.
+ * Author: Rick Troth, Houston, Texas, USA
+ * Date: 1995-Apr-19
+ *
+ * See also: getline.c, putline.c, netline.c
+ */
+int tcpputs(s,b)
+ int s;
+ char *b;
+ {
+ int i, j;
+ char temp[4096];
+
+ for (i = 0; b[i] != 0x00; i++) temp[i] = b[i];
+ temp[i+0] = '\r';
+ temp[i+1] = '\n';
+ j = write(s,temp,i+2);
+
+ if (j != i+2) return -1;
+ return i;
+ }
+
+/* ------------------------------------------------------------ TCPWRITE
+ */
+int tcpwrite(fd,s,n)
+ int fd; char *s; int n;
+ {
+ return write(fd,s,n);
+ }
+
+/* ------------------------------------------------------------- TCPREAD
+ */
+int tcpread(fd,s,n)
+ int fd; char *s; int n;
+ {
+ return read(fd,s,n);
+ }
+
+/* ------------------------------------------------------------ TCPIDENT
+ *
+ * Name: tcpident.c
+ * who's on the other end of this TCP socket?
+ * Author: Rick Troth, Rice University, Information Systems
+ * Date: 1995-Apr-19
+ *
+ * This is the part that was done on Rice time,
+ * prompting the "R" in the version string.
+ * It was to shore-up the last requirements
+ * for the implementation that Rice might keep.
+ * Sadly (to me) someone yanked it (UFT entirely)
+ * the very first day I was gone.
+ */
+
+#ifndef NULL
+#define NULL 0x0000
+#endif
+
+#define HOST_BSZ 128
+#define USER_BSZ 64
+#define TEMP_BSZ 256
+
+#define IDENT_PORT 113
+
+/* ------------------------------------------------------------ TCPIDENT
+ */
+int tcpident(sock,buff,size)
+ int sock; char *buff; int size;
+ {
+ struct sockaddr sadr;
+ struct hostent *hent;
+ int i, rc, slen, styp, soff;
+ char temp[TEMP_BSZ];
+ char hadd[16]; /* is that enough? */
+ char host[HOST_BSZ];
+ char user[USER_BSZ];
+ int plcl, prmt;
+ char *p;
+
+/*
+(void) netline(2,">>>>>>>>");
+ */
+
+ /* preload a few storage areas */
+ host[0] = 0x00;
+ user[0] = 0x00;
+
+ /* first, tell me about this end */
+ slen = sizeof(sadr);
+ rc = getsockname(sock,&sadr,&slen);
+ if (rc != 0)
+ {
+/* perror("getsockname()"); */
+ if (rc < 0) return rc;
+ else return -1;
+ }
+ styp = sadr.sa_family;
+
+ /* where's the offset into the address? */
+ switch (styp)
+ {
+ case AF_INET: soff = 2; slen = 4;
+ break;
+ default: soff = 2;
+ break;
+ }
+
+ /* and snag that port number */
+ plcl = 0;
+ for (i = 0; i < soff; i++)
+ plcl = (plcl << 8) + (sadr.sa_data[i] & 0xFF);
+
+/*
+(void) sprintf(temp,"PORT=%d (mine)",plcl);
+(void) netline(2,temp);
+ */
+
+ /* what's the host on the other end? */
+ slen = sizeof(sadr);
+ rc = getpeername(sock,&sadr,&slen);
+ if (rc != 0)
+ {
+/* perror("getpeername()"); */
+ if (rc < 0) return rc;
+ else return -1;
+ }
+ styp = sadr.sa_family;
+
+ /* where's the offset into the address? */
+ switch (styp)
+ {
+ case AF_INET: soff = 2; slen = 4;
+ break;
+ default: soff = 2;
+ break;
+ }
+
+ /* now copy the address */
+ for (i = 0; i < slen; i++)
+ hadd[i] = sadr.sa_data[i+soff];
+
+ /* and snag that port number */
+ prmt = 0;
+ for (i = 0; i < soff; i++)
+ prmt = (prmt << 8) + (sadr.sa_data[i] & 0xFF);
+
+/*
+(void) sprintf(temp,"PORT=%d (yours)",prmt);
+(void) netline(2,temp);
+ */
+
+ /* what host is at that address? */
+ hent = gethostbyaddr(hadd,slen,styp);
+ if (hent == NULL)
+ {
+/* perror("gethostbyaddr()"); */
+ if (rc < 0) return rc;
+ else return -1;
+ }
+ strncpy(host,hent->h_name,HOST_BSZ); /* keep it */
+ host[HOST_BSZ-1] = 0x00; /* safety net */
+
+/*
+(void) sprintf(temp,"HOST=%s (yours)",host);
+(void) netline(2,temp);
+ */
+
+ /* try a little IDENT client/server action */
+ (void) sprintf(temp,"%s:%d",host,IDENT_PORT);
+ sock = tcpopen(temp,0,0);
+ if (sock >= 0)
+ {
+ /* build and send the IDENT request */
+ (void) sprintf(temp,"%d , %d",prmt,plcl);
+/* (void) netline(sock,temp); */
+ (void) tcpputs(sock,temp);
+/* (void) getline(sock,temp,TEMP_BSZ); */
+ (void) tcpgets(sock,temp,TEMP_BSZ);
+/* (void) netline(1,temp); */
+
+ for (p = temp; *p != 0x00 && *p != ':'; p++);
+ if (*p == ':')
+ {
+ p++;
+ while (*p != 0x00 && *p <= ' ') p++;
+/* (void) netline(2,p); */
+ if (strncmp(p,"USERID",6) == 0)
+ {
+ while (*p != 0x00 && *p != ':') p++;
+ if (*p == ':') p++;
+ while (*p != 0x00 && *p != ':') p++;
+ if (*p == ':') p++;
+ while (*p != 0x00 && *p <= ' ') p++;
+ (void) strncpy(user,p,USER_BSZ);
+ }
+ }
+ }
+
+ (void) sprintf(buff,"%s@%s",user,host);
+
+/*
+(void) netline(2,"<<<<<<<<");
+ */
+
+ return 0;
+ }
+
diff --git a/vmworkshop-vmarcs/1996/msg/tell.exec b/vmworkshop-vmarcs/1996/msg/tell.exec
new file mode 100644
index 0000000..8ce6dbf
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/tell.exec
@@ -0,0 +1,33 @@
+/* © Copyright 1995, Richard M. Troth, all rights reserved.
+ *
+ * Name: TELL EXEC
+ * an RSCS *and* TCP/IP based replacement for CMS TELL
+ * Author: Rick Troth, Houston, Texas, USA
+ * Date: 1992-May-27, Jun-05, Oct-27, Dec-09, 1993-Feb-24
+ * 1995-May-02
+ *
+ * Note: See RFC 1312 for details of the Message Send protocol.
+ *
+ * Co-reqs: TELL REXX, HOSTNAME EXEC, USERLIST REXX
+ */
+
+Parse Upper Arg . at .
+
+If at = "AT" Then Do
+ Parse Arg user . node message
+ If message = "" Then ,
+ 'PIPE CONSOLE ASYNCH | TELL' user 'AT' node '| CONSOLE'
+ Else ,
+ 'PIPE VAR MESSAGE | TELL' user 'AT' node '| CONSOLE'
+ End /* If .. Do */
+
+Else Do
+ Parse Arg user message
+ If message = "" Then ,
+ 'PIPE CONSOLE ASYNCH | TELL' user '| CONSOLE'
+ Else ,
+ 'PIPE VAR MESSAGE | TELL' user '| CONSOLE'
+ End /* Else Do */
+
+Exit rc
+
diff --git a/vmworkshop-vmarcs/1996/msg/tell.helpcms b/vmworkshop-vmarcs/1996/msg/tell.helpcms
new file mode 100644
index 0000000..f3c8426
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/tell.helpcms
@@ -0,0 +1,28 @@
+.cm Copyright 1993, Richard M. Troth
+.cm
+
+ TELL EXEC
+
+ Use the TELL command to send interactive messages to other users
+ on this or other BITNET or InterNet connected computers.
+
+ The format of the TELL command is:
+ +-------------------------------------+
+ | | |
+ | TELL | user Ý[message]¨ |
+ | | |
+ +-------------------------------------+
+
+ where:
+
+ user
+ is the user or nickname of a list of users
+ to whom you wish to send interactive messages
+
+ message
+ is an optional one-line message
+ If the message is omitted, TELL puts your virtual machine
+ into "chat mode" such that all lines entered at the console
+ are sent to the specified user. Terminate by pressing ENTER
+ twice on a blank line (no input; Pipeline CONSOLE EOF).
+
diff --git a/vmworkshop-vmarcs/1996/msg/tell.rexx b/vmworkshop-vmarcs/1996/msg/tell.rexx
new file mode 100644
index 0000000..fc0552d
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/tell.rexx
@@ -0,0 +1,280 @@
+/* © Copyright 1995, Richard M. Troth, all rights reserved.
+ *
+ * Name: TELL REXX
+ * send interactive messages to other users
+ * Author: Rick Troth, Houston, Texas, USA
+ * Rick Troth, Houston, Texas, USA
+ * Date: 1993-Feb-24, and prior, 1993-Aug-27
+ * 1995-May-02
+ */
+
+Parse Arg userlist
+'CALLPIPE VAR USERLIST | USERLIST | STEM USER.'
+
+'CALLPIPE COMMAND IDENTIFY | VAR IDENTITY'
+Parse Var identity userid . nodeid . rscsid .
+'CALLPIPE VAR USERID | XLATE LOWER | VAR LOCALUSER'
+localhost = hostname()
+
+Parse Value diag(08,'QUERY VIRTUAL CONSOLE') ,
+ With . vaddr on type raddr term start . '15'x .
+If on = "DISCONNECTED" Then localterm = "Not connected"
+ Else localterm = raddr
+
+'CALLPIPE COMMAND GLOBALV SELECT' "$" || Userid() ,
+ 'LIST TELL | DROP | VAR OPTIONS'
+Parse Var options . "MSGCMD" msg .
+If msg = "" Then msg = "MSG"
+
+/* ---------------------------------------------------------------- TELL
+ */
+Do Forever
+
+ 'PEEKTO MESSAGE'
+ If rc ^= 0 Then Leave
+ If Strip(message) = "." Then Leave
+
+ Do i = 1 to user.0
+ Parse Var user.i user '@' node
+ If user = "" Then Iterate
+ Select /* node */
+ When node = "" Then Do
+ Upper user
+ Parse Value Diagrc(08, msg user message) ,
+ With 1 rc 10 . 17 rs '15'x .
+ If rc ^= 0 & rs ^= "" Then 'OUTPUT' rs
+ End /* When .. Do */
+ When Index(node,'.') = 0 Then Do
+ If user = '*' Then user = userid
+ If node = '*' Then node = nodeid
+ Upper user node
+ Parse Value Diagrc(08,'SMSG' rscsid 'MSG' ,
+ node user message) With 1 rc 10 . 17 rs '15'x .
+ If rc ^= 0 & node = nodeid Then
+ Parse Value Diagrc(08, msg user message) ,
+ With 1 rc 10 . 17 rs '15'x .
+ If rc ^= 0 & rs ^= "" Then 'OUTPUT' rs
+ End /* When .. Do */
+ Otherwise Do
+ If user = '*' Then user = localuser
+ If node = '*' Then node = localhost
+ Call VIA_MSGD user, node, message
+ End /* Otherwise Do */
+ End /* Select node */
+ End /* Do For */
+
+ 'READTO'
+
+ End /* Do Forever */
+
+Exit rc * (rc ^= 12)
+
+
+
+/* ------------------------------------------------------------ VIA_MSGD
+ */
+VIA_MSGD: Procedure Expose localuser localterm
+Parse Arg user, host, text
+ver = 'B'
+term = '*'
+port = 18
+
+/*
+ * Verify REXX/Sockets (RXSOCKET version 2).
+ */
+Parse Value Socket("VERSION") With rc . rv .
+v1 = (rv < 2)
+
+/*
+ * Initialize RXSOCKET
+ */
+If v1 Then Do
+ maxdesc = Socket('Initialize', 'MessageC')
+ If maxdesc = "-1" Then Do
+ If errno ^= "ESUBTASKALREADYACTIVE" Then Do
+ Say tcperror()
+ Return -1
+ End /* If .. Do */
+ rc = Socket('Terminate')
+ maxdesc = Socket('Initialize', 'MessageC')
+ End /* If .. Do */
+ If maxdesc = "-1" Then Do
+ Say tcperror()
+ Return -1
+ End /* If .. Do */
+ End /* If .. Do */
+
+Else Do
+ Parse Value Socket("INITIALIZE", "MessageC") With rc errno maxdesc .
+ If rc ^= 0 Then Do
+ If errno ^= "ESUBTASKALREADYACTIVE" Then Do
+ Say tcperror()
+ Return rc
+ End /* If .. Do */
+ Parse Value Socket("TERMINATE") With rc .
+ Parse Value Socket("INITIALIZE", "MessageC") With rc . maxdesc .
+ If rc ^= 0 Then Do
+ Say tcperror()
+ Return rc
+ End /* If .. Do */
+ End /* If .. Do */
+ End /* Else Do */
+
+/*
+ * Get a socket descriptor (TCP protocol)
+ */
+If v1 Then Do
+ socket = Socket('Socket', 'AF_INET', 'Sock_Stream')
+ If socket = "-1" Then Return -1
+ End /* If .. Do */
+
+Else Do
+ Parse Value Socket("SOCKET", "AF_INET", "SOCK_STREAM") ,
+ With rc socket .
+ If rc ^= 0 Then Do
+ Say tcperror()
+ Parse Value Socket("TERMINATE") With rc .
+ Return rc
+ End /* If .. Do */
+ End /* Else Do */
+
+/*
+ * Enable ASCII<->EBCDIC translation option
+ */
+If v1 Then Do
+ rc = Socket('SetSockOpt', socket, 'SOL_SOCKET', 'SO_EBCDIC', 1)
+ If rc = "-1" Then Return -1
+ End /* If .. Do */
+
+Else Do
+ Parse Value Socket("SETSOCKOPT", socket, "SOL_SOCKET", ,
+ "SO_ASCII", "ON") With rc .
+ If rc ^= 0 Then Do
+ Say tcperror()
+ Parse Value Socket("CLOSE", socket) With rc .
+ Parse Value Socket("TERMINATE") With rc .
+ Return rc
+ End /* If .. Do */
+ End /* Else Do */
+
+/*
+ * Figure out the target host address
+ */
+If v1 Then Do
+ Parse Var host h1 '.' h2 '.' h3 '.' h4 '.' .
+ If Datatype(h1,'N') &,
+ Datatype(h2,'N') &,
+ Datatype(h3,'N') &,
+ Datatype(h4,'N') Then
+ hisaddr = d2c(h1) || d2c(h2) || d2c(h3) || d2c(h4)
+ Else Do
+ hisaddr = Socket('GetHostByName', host)
+ If hisaddr = "-1" Then Return -1
+ End /* Else Do */
+ End /* If .. Do */
+
+Else Do
+ Parse Value Socket("RESOLVE", host) With rc hisaddr hisname .
+ If rc ^= 0 Then Do
+ Say tcperror()
+ Parse Value Socket("CLOSE", socket) With rc .
+ Parse Value Socket("TERMINATE") With rc .
+ Return rc
+ End /* If .. Do */
+ End /* Else Do */
+
+/*
+ * Connect to the MessageD server.
+ */
+If v1 Then Do
+ rc = Socket('Connect', socket, AF_INET || Htons(port) || hisaddr)
+ If rc = "-1" Then Return -1
+ End /* If .. Do */
+
+Else Do
+ Parse Value Socket("CONNECT", socket, "AF_INET" port hisaddr) ,
+ With rc .
+ If rc ^= 0 Then Do
+ Say tcperror()
+ Parse Value Socket("CLOSE", socket) With .
+ Parse Value Socket("TERMINATE") With .
+ Return rc
+ End /* If .. Do */
+ End /* Else Do */
+
+/*
+ * Compose the message packet.
+ */
+data = ver || user || '00'x || term || '00'x || text || '00'x || ,
+ localuser || '00'x || localterm || '00'x || ,
+ Time('S') || '00'x || "?" '00'x
+
+/*
+ * Send the message.
+ */
+If v1 Then Do
+ bc = Socket('Write', socket, data)
+ If bc = "-1" Then Return -1
+ End /* If .. Do */
+
+Else Do
+ Parse Value Socket("WRITE", socket, data) With rc bc .
+ If rc ^= 0 Then Do
+ Say tcperror()
+ Parse Value Socket("CLOSE", socket) With rc .
+ Parse Value Socket("TERMINATE") With rc .
+ Return rc
+ End /* If .. Do */
+ End /* Else Do */
+
+/*
+ * Recover some response (if available).
+ */
+If v1 Then Do
+ bc = Socket('Read', socket, 'DATA')
+ If bc = "-1" Then Return -1
+ End /* If .. Do */
+
+Else Do
+ Parse Value Socket("READ", socket) With rc bc data
+ If rc ^= 0 Then Do
+ Say tcperror()
+ Parse Value Socket("CLOSE", socket) With rc .
+ Parse Value Socket("TERMINATE") With rc .
+ Return rc
+ End /* If .. Do */
+ End /* Else Do */
+
+/*
+ * Display the response (if any).
+ */
+If bc > 0 Then
+ If Left(data,1) ^= '+' Then Do
+ If Left(data,1) = '-' Then data = Substr(data,2)
+ Parse Var data data '00'x .
+ 'CALLPIPE VAR DATA | STRIP BOTH 25' ,
+ '| XLATE *-* 25 15 | VAR DATA'
+ Say data
+ End /* If .. Do */
+
+/*
+ * All done, relinquish our socket descriptor
+ */
+Parse Value Socket("CLOSE", socket) With rc .
+If rc ^= 0 Then Do
+ Say tcperror()
+ Parse Value Socket("TERMINATE") With .
+ Return rc
+ End /* If .. Do */
+
+/*
+ * Tell REXX/Sockets that we are done with this IUCV path.
+ */
+Parse Value Socket("TERMINATE") With rc .
+If rc ^= 0 Then Do
+ Say tcperror()
+ Return rc
+ End /* If .. Do */
+
+Return 0
+
diff --git a/vmworkshop-vmarcs/1996/msg/userid.c b/vmworkshop-vmarcs/1996/msg/userid.c
new file mode 100644
index 0000000..bd6e91c
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/userid.c
@@ -0,0 +1,66 @@
+/* © Copyright 1995, Richard M. Troth, all rights reserved.
+ *
+ * Name: userid.c
+ * return the login name associated with this process
+ * Author: Rick Troth, Rice University, Information Systems
+ * Date: 1994-Jul-26
+ *
+ * 1995-Apr-17: added useridg() function
+ */
+
+#include
+
+/* -------------------------------------------------------------- USERID
+ * return login name from the best of several usable sources
+ */
+char *userid()
+ {
+ char *u;
+ extern char *getenv();
+ struct passwd *pwdent;
+
+ /* first try effective uid key into passwd */
+ pwdent = getpwuid(geteuid());
+ if (pwdent) return pwdent->pw_name;
+
+ /* next try real uid key into passwd */
+ pwdent = getpwuid(getuid());
+ if (pwdent) return pwdent->pw_name;
+
+ /* thin ice, try USER env var */
+ u = getenv("USER");
+ if (u != 0x0000 && u[0] != 0x00) return u;
+
+ /* last resort, try LOGNAME env var */
+ u = getenv("LOGNAME");
+ if (u != 0x0000 && u[0] != 0x00) return u;
+
+ /* give up! */
+ return "";
+ }
+
+/* ------------------------------------------------------------- USERIDG
+ * "g" for GECOS field, return personal name string, if available
+ */
+char *useridg()
+ {
+ char *g;
+ extern char *getenv();
+ struct passwd *pwdent;
+
+ /* if the user set one, take that */
+ g = getenv("NAME");
+ if (g != 0x0000 && *g != 0x00) return g;
+
+ /* next, try GECOS for effective uid key into passwd */
+ pwdent = getpwuid(geteuid());
+ if (pwdent) return pwdent->pw_gecos;
+
+ /* next, try GECOS field for real uid key into passwd */
+ pwdent = getpwuid(getuid());
+ if (pwdent) return pwdent->pw_gecos;
+
+ /* give up! */
+ return userid();
+ }
+
diff --git a/vmworkshop-vmarcs/1996/msg/userlist.rexx b/vmworkshop-vmarcs/1996/msg/userlist.rexx
new file mode 100644
index 0000000..65ab3e2
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/msg/userlist.rexx
@@ -0,0 +1,45 @@
+/*
+ * Name: USERLIST REXX
+ * generates a list of users from nickname input
+ * Author: Rick Troth, Houston, Texas, USA
+ * Date: 1992-Oct-02, Dec-09
+ *
+ * Note: this does not presently support the TCPADDR hack
+ */
+
+Parse Arg list '(' opts
+Parse Source . . arg0 .
+
+If list ^= "" Then 'CALLPIPE VAR LIST |' arg0 '| *:'
+
+'ADDPIPE *: | CHANGE /@/ AT / | SPLIT | *.INPUT:'
+
+Do Forever
+
+ 'READTO USER'
+ If rc ^= 0 Then Leave
+ If user = "" Then Iterate
+
+ 'PEEKTO AT'; Upper at
+ If rc = 0 & at = "AT" Then Do
+ 'READTO'
+ 'READTO HOST'
+ If rc ^= 0 | host = "" Then 'OUTPUT' user
+ Else 'OUTPUT' user || '@' || host
+ End /* If .. Do */
+ Else Do
+ nick = user
+ 'CALLPIPE CMS NAMEFIND :NICK' nick ':USERID :NODE :LIST' ,
+ '| VAR USER | DROP | VAR HOST | DROP | VAR LIST'
+ If rc = 0 Then Do
+ If host = "" Then 'OUTPUT' user
+ Else 'OUTPUT' user || '@' || host
+ If list ^= "" Then 'CALLPIPE VAR LIST |' arg0 '| *:'
+ End /* If .. Do */
+ Else 'OUTPUT' nick
+ End /* Else Do */
+
+ End /* Do Forever */
+
+Exit rc * (rc ^= 12)
+
diff --git a/vmworkshop-vmarcs/1996/pf10pf11/pf10.xedit b/vmworkshop-vmarcs/1996/pf10pf11/pf10.xedit
new file mode 100644
index 0000000..e429ea8
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/pf10pf11/pf10.xedit
@@ -0,0 +1,107 @@
+/* PF10 */
+"REFRESH"
+"EXTRACT /VERSHIFT/"
+IF VERSHIFT.1<0 THEN "RIGHT "ABS(VERSHIFT.1)
+ ELSE DO
+ "EXTRACT /CMDLINE/CURSOR/"
+ IF CURSOR.1=CMDLINE.2 THEN DO
+ "EXTRACT /VERIFY/"
+ IF WORDS(VERIFY.2)=2 THEN DO
+ "PIPE VAR VERIFY.2",
+ "| SPLIT AT ANYOF / H/",
+ "| JOIN 1 / /",
+ "| STEM TABLE1."
+ IF VERSHIFT.1>0 THEN DO
+ "EXTRACT /LRECL/"
+ SELECT
+ WHEN LRECL.1<=WORD(TABLE1.1,2) THEN "LEFT "VERSHIFT.1
+ WHEN LRECL.1=WORD(TABLE1.1,2)-WORD(TABLE1.1,1) THEN "LEFT "WORD(TABLE1.1,2)-WORD(TABLE1.1,1)
+ OTHERWISE "LEFT "VERSHIFT.1
+ END
+ END
+ END
+ END
+ ELSE DO
+ "EXTRACT /SIZE/"
+ IF CURSOR.7=-1 |,
+ CURSOR.7>SIZE.1 |,
+ CURSOR.8=-1 THEN "CURSOR CMDLINE 1"
+ ELSE DO
+ "EXTRACT /LSCREEN/PREFIX/VERIFY/"
+ "PIPE VAR VERIFY.2",
+ "| SPLIT",
+ "| JOIN 1 / /",
+ "| STEM TABLE1."
+ IF PREFIX.1="ON" &,
+ PREFIX.2="LEFT" THEN COUNT1=6
+ ELSE COUNT1=0
+ DO COUNT2=1 FOR LSCREEN.1
+ "CURSOR SCREEN "COUNT2" 1"
+ "EXTRACT /CURSOR/"
+ IF CURSOR.3=CURSOR.7 THEN LEAVE COUNT2
+ END COUNT2
+ COUNT3=LSCREEN.2*(CURSOR.5-CURSOR.1)+CURSOR.6-COUNT1
+ COUNT4=0
+ COUNT5=COUNT1+1
+ DO COUNT6=1 FOR TABLE1.0
+ IF SUBSTR(TABLE1.COUNT6,1,1)="H" THEN DO
+ TABLE1.COUNT6=TRANSLATE(TABLE1.COUNT6,,"H")
+ COUNT4=COUNT4+(WORD(TABLE1.COUNT6,2)-WORD(TABLE1.COUNT6,1)+1)*2
+ COUNT7=2
+ END
+ ELSE DO
+ COUNT4=COUNT4+WORD(TABLE1.COUNT6,2)-WORD(TABLE1.COUNT6,1)+1
+ COUNT7=1
+ END
+ IF COUNT4>=COUNT3 THEN LEAVE COUNT6
+ COUNT5=COUNT5+WORD(TABLE1.COUNT6,2)-WORD(TABLE1.COUNT6,1)+1
+ END COUNT6
+ SELECT
+ WHEN VERSHIFT.1=0 THEN COUNT8=(CURSOR.8-WORD(TABLE1.COUNT6,1))*COUNT7
+ WHEN WORD(TABLE1.COUNT6,1)=WORD(TABLE1.COUNT6,2) THEN DO
+ "LEFT 1"
+ COUNT8=0
+ END
+ WHEN VERSHIFT.1<=WORD(TABLE1.COUNT6,2)-WORD(TABLE1.COUNT6,1) THEN DO
+ SELECT
+ WHEN CURSOR.8=VERSHIFT.1+WORD(TABLE1.COUNT6,2) THEN DO
+ "LEFT "VERSHIFT.1
+ COUNT8=0
+ END
+ WHEN CURSOR.8<=WORD(TABLE1.COUNT6,2) THEN DO
+ "LEFT "VERSHIFT.1
+ COUNT8=-VERSHIFT.1*COUNT7
+ END
+ OTHERWISE DO
+ "LEFT "VERSHIFT.1+WORD(TABLE1.COUNT6,2)-CURSOR.8
+ COUNT8=-(VERSHIFT.1+WORD(TABLE1.COUNT6,2)-CURSOR.8)*COUNT7
+ END
+ END
+ END
+ WHEN CURSOR.8=VERSHIFT.1+WORD(TABLE1.COUNT6,2) THEN DO
+ "LEFT "WORD(TABLE1.COUNT6,2)-WORD(TABLE1.COUNT6,1)
+ COUNT8=0
+ END
+ OTHERWISE DO
+ "LEFT "VERSHIFT.1+WORD(TABLE1.COUNT6,2)-CURSOR.8
+ COUNT8=-(VERSHIFT.1+WORD(TABLE1.COUNT6,2)-CURSOR.8)*COUNT7
+ END
+ END
+ DO COUNT9=1 FOR LSCREEN.1
+ "CURSOR SCREEN "COUNT9" 1"
+ "EXTRACT /CURSOR/"
+ IF CURSOR.3=CURSOR.7 THEN LEAVE COUNT9
+ END COUNT9
+ COUNT10=LSCREEN.2*(COUNT9-CURSOR.1)+COUNT3-COUNT8+COUNT1
+ IF COUNT10//LSCREEN.2=0 THEN "CURSOR SCREEN "CURSOR.1+TRUNC((COUNT10-1)/LSCREEN.2)" "LSCREEN.2
+ ELSE "CURSOR SCREEN "CURSOR.1+TRUNC((COUNT10-1)/LSCREEN.2)" "COUNT10//LSCREEN.2
+ "EXTRACT /CURSOR/"
+ IF CURSOR.3=-1 |,
+ CURSOR.4=-1 THEN "CURSOR CMDLINE 1"
+ END
+ END
+END
+"REFRESH"
diff --git a/vmworkshop-vmarcs/1996/pf10pf11/pf11.xedit b/vmworkshop-vmarcs/1996/pf10pf11/pf11.xedit
new file mode 100644
index 0000000..acd096d
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/pf10pf11/pf11.xedit
@@ -0,0 +1,124 @@
+/* PF11 */
+"REFRESH"
+"EXTRACT /VERSHIFT/"
+IF VERSHIFT.1<0 THEN "RIGHT "ABS(VERSHIFT.1)
+ ELSE DO
+ "EXTRACT /CMDLINE/CURSOR/"
+ IF CURSOR.1=CMDLINE.2 THEN DO
+ "EXTRACT /VERIFY/"
+ IF WORDS(VERIFY.2)=2 THEN DO
+ "PIPE VAR VERIFY.2",
+ "| SPLIT AT ANYOF / H/",
+ "| JOIN 1 / /",
+ "| STEM TABLE1."
+ "EXTRACT /LRECL/"
+ IF LRECL.1/=VERSHIFT.1+WORD(TABLE1.1,2) THEN DO
+ SELECT
+ WHEN LRECL.1<=WORD(TABLE1.1,2) THEN IF VERSHIFT.1>0 THEN "LEFT "VERSHIFT.1
+ WHEN LRECL.1SIZE.1 |,
+ CURSOR.8=-1 THEN "CURSOR CMDLINE 1"
+ ELSE DO
+ "EXTRACT /LRECL/LSCREEN/PREFIX/VERIFY/"
+ "PIPE VAR VERIFY.2",
+ "| SPLIT",
+ "| JOIN 1 / /",
+ "| STEM TABLE1."
+ IF PREFIX.1="ON" &,
+ PREFIX.2="LEFT" THEN COUNT1=6
+ ELSE COUNT1=0
+ DO COUNT2=1 FOR LSCREEN.1
+ "CURSOR SCREEN "COUNT2" 1"
+ "EXTRACT /CURSOR/"
+ IF CURSOR.3=CURSOR.7 THEN LEAVE COUNT2
+ END COUNT2
+ COUNT3=LSCREEN.2*(CURSOR.5-CURSOR.1)+CURSOR.6-COUNT1
+ COUNT4=0
+ COUNT5=COUNT1+1
+ DO COUNT6=1 FOR TABLE1.0
+ IF SUBSTR(TABLE1.COUNT6,1,1)="H" THEN DO
+ TABLE1.COUNT6=TRANSLATE(TABLE1.COUNT6,,"H")
+ COUNT4=COUNT4+(WORD(TABLE1.COUNT6,2)-WORD(TABLE1.COUNT6,1)+1)*2
+ COUNT7=2
+ END
+ ELSE DO
+ COUNT4=COUNT4+WORD(TABLE1.COUNT6,2)-WORD(TABLE1.COUNT6,1)+1
+ COUNT7=1
+ END
+ IF COUNT4>=COUNT3 THEN LEAVE COUNT6
+ COUNT5=COUNT5+WORD(TABLE1.COUNT6,2)-WORD(TABLE1.COUNT6,1)+1
+ END COUNT6
+ SELECT
+ WHEN LRECL.1=VERSHIFT.1+WORD(TABLE1.COUNT6,2) THEN COUNT8=(CURSOR.8-VERSHIFT.1-WORD(TABLE1.COUNT6,1))*COUNT7
+ WHEN LRECL.1<=WORD(TABLE1.COUNT6,2) THEN DO
+ IF VERSHIFT.1>0 THEN "LEFT "VERSHIFT.1
+ COUNT8=(CURSOR.8-WORD(TABLE1.COUNT6,1)-VERSHIFT.1)*COUNT7
+ END
+ WHEN LRECL.1LRECL.1-(WORD(TABLE1.1,2)*2-WORD(TABLE1.1,1)) THEN DO
+ SELECT
+ WHEN CURSOR.8=VERSHIFT.1+WORD(TABLE1.COUNT6,1) THEN DO
+ "RIGHT "LRECL.1-(VERSHIFT.1+WORD(TABLE1.COUNT6,2))
+ COUNT8=0
+ END
+ WHEN CURSOR.8<=VERSHIFT.1+LRECL.1-(VERSHIFT.1+WORD(TABLE1.COUNT6,2)) THEN DO
+ "RIGHT "CURSOR.8-VERSHIFT.1-WORD(TABLE1.COUNT6,1)
+ COUNT8=(CURSOR.8-VERSHIFT.1-WORD(TABLE1.COUNT6,1))*COUNT7
+ END
+ OTHERWISE DO
+ "RIGHT "LRECL.1-(VERSHIFT.1+WORD(TABLE1.COUNT6,2))
+ COUNT8=(LRECL.1-(VERSHIFT.1+WORD(TABLE1.COUNT6,2)))*COUNT7
+ END
+ END
+ END
+ OTHERWISE DO
+ SELECT
+ WHEN CURSOR.8=VERSHIFT.1+WORD(TABLE1.COUNT6,1) THEN DO
+ "RIGHT "WORD(TABLE1.COUNT6,2)-WORD(TABLE1.COUNT6,1)
+ COUNT8=0
+ END
+ WHEN CURSOR.8=VERSHIFT.1+WORD(TABLE1.COUNT6,2) THEN DO
+ "RIGHT "WORD(TABLE1.COUNT6,2)-WORD(TABLE1.COUNT6,1)
+ COUNT8=(WORD(TABLE1.COUNT6,2)-WORD(TABLE1.COUNT6,1))*COUNT7
+ END
+ OTHERWISE DO
+ "RIGHT "CURSOR.8-VERSHIFT.1-WORD(TABLE1.COUNT6,1)
+ COUNT8=(CURSOR.8-VERSHIFT.1-WORD(TABLE1.COUNT6,1))*COUNT7
+ END
+ END
+ END
+ END
+ DO COUNT9=1 FOR LSCREEN.1
+ "CURSOR SCREEN "COUNT9" 1"
+ "EXTRACT /CURSOR/"
+ IF CURSOR.3=CURSOR.7 THEN LEAVE COUNT9
+ END COUNT9
+ COUNT10=LSCREEN.2*(COUNT9-CURSOR.1)+COUNT3-COUNT8+COUNT1
+ IF COUNT10//LSCREEN.2=0 THEN "CURSOR SCREEN "COUNT9+TRUNC((COUNT10-1)/LSCREEN.2)" "LSCREEN.2
+ ELSE "CURSOR SCREEN "COUNT9+TRUNC((COUNT10-1)/LSCREEN.2)" "COUNT10//LSCREEN.2
+ "EXTRACT /CURSOR/"
+ IF CURSOR.3=-1 |,
+ CURSOR.4=-1 THEN "CURSOR CMDLINE 1"
+ END
+ END
+END
+"REFRESH"
diff --git a/vmworkshop-vmarcs/1996/spbackup/README.md b/vmworkshop-vmarcs/1996/spbackup/README.md
new file mode 100644
index 0000000..11c16a2
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/README.md
@@ -0,0 +1,11 @@
+# SPBACKUP
+
+Spool Backup System, using SPXTAPE
+University of Illinois at Chicago
+June 22, 1996
+
+
+SPBACKUP is a spool backup system which uses the new SPXTAPE command to back up the spool. It's purpose is to create an interface between the CP SPXTAPE command, and the VMTAPE tape system, and to create catalogs and indexes to facilitate restores.
+
+Roger Deschner University of Illinois at Chicago rogerd@uic.edu
+Computer Center M/C 135, 1940 W. Taylor St., Room 124, Chicago, IL 60612
diff --git a/vmworkshop-vmarcs/1996/spbackup/abend.email b/vmworkshop-vmarcs/1996/spbackup/abend.email
new file mode 100644
index 0000000..32c6a49
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/abend.email
@@ -0,0 +1,8 @@
+Subject: SPBACKUP Failure Today
+
+SPBACKUP reports a premature end. Please check its logs in SYSLOG and in
+its reader to be sure it ran OK.
+
+We are going to attempt to run the WRAPUP step, however it might not
+work. It may be necessary to run the WRAPUP step over again after
+figuring out why the job failed.
\ No newline at end of file
diff --git a/vmworkshop-vmarcs/1996/spbackup/backmoun.exec b/vmworkshop-vmarcs/1996/spbackup/backmoun.exec
new file mode 100644
index 0000000..114216e
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/backmoun.exec
@@ -0,0 +1,28 @@
+/* BACKMOUN EXEC:
+We are trapping the following two VMTAPE MSNGOH's:
+
+VMTMNT073I Volume 'MVS1' ready on 0181 (1882) R/W SL.
+VMTBUF016I MOUNT completion code=0.
+
+MODIFICATION HISTORY:
+02/13/96 - Roger Deschner - Original Version
+*/
+ADDRESS COMMAND
+PARSE ARG msgl
+msgl = STRIP(msgl)
+'GLOBALV SELECT SPBACKUP GET MODEMOUN' /* Were we recording already? */
+SELECT
+ WHEN (modemoun = 'RECORD') THEN DO /*Yes, we were recording already.*/
+ 'PIPE VAR msgl | >> BACKMOUN SAVEMSGS A' /* Add to msg queue */
+ IF (SUBSTR(msgl,1,10) = 'VMTBUF016I') THEN DO /* Done? */
+ 'EXEC BACKUP MOUNTED' /* Doit toit */
+ 'GLOBALV SELECT SPBACKUP SETL MODEMOUN NORMAL' /* Stop recoding */
+ END
+ END
+ WHEN (SUBSTR(msgl,1,10) = 'VMTMNT073I') THEN DO
+ 'GLOBALV SELECT SPBACKUP SETL MODEMOUN RECORD' /* Start recording */
+ 'PIPE VAR msgl | > BACKMOUN SAVEMSGS A' /* Start new msg queue */
+ END
+ OTHERWISE NOP
+END
+EXIT
diff --git a/vmworkshop-vmarcs/1996/spbackup/backtrap.exec b/vmworkshop-vmarcs/1996/spbackup/backtrap.exec
new file mode 100644
index 0000000..ab3d8ea
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/backtrap.exec
@@ -0,0 +1,67 @@
+/* BACKTRAP EXEC:
+This routine collects CP console output.
+
+MODIFICATION HISTORY:
+05/13/96 - Roger Deschner - Add trap for premature end
+04/17/96 - Roger Deschner - Add more detailed trap for reader files
+02/13/96 - Roger Deschner - Original Version
+*/
+ADDRESS COMMAND
+PARSE ARG msgl
+msgl = STRIP(msgl)
+'PIPE VAR msgl', /* DEBUG TRACE */
+ '| SPECS !' || DATE('U') TIME() || '! 1',
+ '1-* NEXTWORD',
+ '| >> BACKTRAP LOG A'
+/* '| CONSOLE' */
+'GLOBALV SELECT SPBACKUP GET MODETRAP' /* Were we recording already? */
+SELECT
+ WHEN (SUBSTR(msgl,1,9) = 'RDR FILE ') THEN DO
+ PARSE VAR msgl . . spid . . originid .
+ IF (originid = USERID()) THEN DO /* Did it come from ourselves? */
+
+/* Sample output:
+ORIGINID FILE CLASS RECORDS CPY HOLD DATE TIME NAME TYPE DIST
+SPBACKUP 0078 T CON 00010985 001 NONE 04/17 03:06:47 0417D182 02184301 UICVM
+*/
+
+ 'PIPE VAR msgl | >> BACKFILE SAVEMSGS A'
+ 'PIPE CP QUERY RDR *' spid 'ALL',
+ '| DROP FIRST 1', /* eliminate header */
+ '| LOCATE 15-19 /T CON/', /* Just the ones we really want */
+ '| LOCATE 58 /D/', /* Take just files from DUMP commands */
+ '| NLOCATE 58-61 /DUMP/', /* Eliminate Cmd Summary logs */
+ '| SPECS 54-71 1', /* Just the fileid */
+ '| VAR spfileid',
+ '| COUNT LINES', /* See if we got one */
+ '| VAR nspfileid'
+ IF (nspfileid = 1) THEN DO
+ SAY 'BACKTRAP: Sending unload signal. Received file' spfileid
+ 'EXEC BACKUP UNLOADED' spfileid
+ END
+ END
+ END
+ WHEN (modetrap = 'RECORD') THEN DO /*Yes, we were recording already. */
+ 'PIPE VAR msgl | >> BACKTRAP SAVEMSGS A' /* Add to msg queue */
+ IF (SUBSTR(msgl,1,12) = 'SPOOL PAGES:') THEN DO /* Done? */
+ SAY 'BACKTRAP received a complete message from SPXTAPE:'
+ 'PIPE < BACKTRAP SAVEMSGS A | SPECS />/ 1 1-* 2 | CONSOLE'
+ /* Clear recording flag in case something comes during EOT. */
+ 'GLOBALV SELECT SPBACKUP SETL MODETRAP NORMAL' /* Stop recoding */
+ 'EXEC BACKUP EOT' /* Doit toit */
+ END
+ END
+ WHEN ((SUBSTR(msgl,1,32) = 'SPXTAPE DUMP END-OF-TAPE ON VDEV'),
+ | (SUBSTR(msgl,1,31) = 'SPXTAPE DUMP COMPLETED ON VDEV'),
+ | (SUBSTR(msgl,1,39) = 'SPXTAPE DUMP COMMAND COMPLETED ON VDEV'),
+ | (SUBSTR(msgl,1,39) = 'SPXTAPE DUMP COMMAND ENDED ON VDEV'),
+ )THEN DO
+ 'GLOBALV SELECT SPBACKUP SETL MODETRAP RECORD' /* Start recording */
+ 'PIPE VAR msgl | > BACKTRAP SAVEMSGS A' /* Start new msg queue */
+ END
+ WHEN (SUBSTR(msgl,1,7) = 'DUMPING') THEN DO /* Save progress msg */
+ 'GLOBALV SELECT SPBACKUP SETL PROGRESS' SUBSTR(msgl,20)
+ END
+ OTHERWISE NOP
+END
+EXIT
diff --git a/vmworkshop-vmarcs/1996/spbackup/backup.exec b/vmworkshop-vmarcs/1996/spbackup/backup.exec
new file mode 100644
index 0000000..d8c5b19
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/backup.exec
@@ -0,0 +1,1074 @@
+/* BACKUP EXEC:
+This is a WAKEUP application which will start, modify, query, or cancel a
+SPXTAPE spool backup.
+
+For users:
+ .--2--------.
+ >>--BACKUP--+--START--tapepool--+--ndrives--+--+--><
+ +--QUERY---------------------------+
+ +--DRIVES--ndrives-----------------+
+ +--CANCEL--------------------------+
+ +--DATA----------------------------+
+ +--DUMP----------------------------+
+ '--WRAPUP--hhmmss--tapepool--------'
+
+For IUCVTRAP action routine exits:
+
+ >>--BACKUP--+--MOUNTED-------------------+--><
+ +--EOT-----------------------+
+ '--UNLOADED--spfileid--------+
+
+...where hhmmss is the time stamp on the log files in the spool.
+
+TAPEPOOL is the filename of a CMS file (filetype TAPEPOOL) containing a
+list of tape volsers. These tapes must be in the VMTAPE Catalog. This
+file may contain comments with * in column one.
+
+The first tape in the tapepool is actually used last, to back up the
+catalog database of this backup.
+
+DATA STRUCTURES (mostly stored in GLOBALV)
+
+(Debugging tip: GLOBALV SELECT SPBACKUP LIST)
+
+qbackup = 1 if a backup is running
+qcomplete = 1 when we receive the first indication of SPXTAPE
+ completing on any drive. At that point, we refuse to start
+ any more new tapes that a sleeping operator woke up and
+ mounted.
+vdev. - stem of virtual device addresses.
+drivevol. - stem of which VOLSER is mounted on each member of vdev.
+drivestat. - stem of status of each member of vdev.
+ IDLE - not in use at all
+ MOUNTING - VMTAPE MOUNT issued; no response yet
+ DUMPING - Dumping currently in process
+ DRAINING - Dump completed to this vol but can't DETACH
+ due to this is the last drive, and the Operator is asleep.
+ There should be some other drive in MOUNTING status. Once
+ the Operator wakes up, mounts the tape, and it goes
+ DUMPING, this one can be DETACHed and made IDLE. Volume
+ Log will likely arrive in the meantime, and if it does,
+ we accept it and say thank you and that's all.
+ UNLOADING - Dump completed to this vol but can't DETACH yet
+ because it is still rewinding and unloading. This must
+ wait until we get the Volume Log in our reader for this
+ tape. When it arrives, we can CP DETACH it. In the
+ meantime, we'll mount our next tape on another vdev.
+ndrives - How many drives the operator wishes to use
+nspxtape - how many drives are actually in use BY SPXTAPE
+ninuse - how many drives are actually in use either mounting or dump
+ Note: ninuse - nspxtape should equal the number of pending mounts.
+tapepool - CMS filename of the current tapepool
+tapevol. - stem of tapes in . This is read over again for
+ each invocation. Therefore, the operator can add tapes to
+ the end of a tapepool if it runs out.
+tapestat. - stem of status of each member of tapevol.
+ UNUSED - not yet written on
+ MOUNTING - Requested for mount but not yet written on
+ WRITING - In the process of being written upon
+ WRITTEN - Completed writing
+ RESERVED - ineligible for writing
+tapevoli - pointer to last tape used in stem tapevol.
+starttime - hhmmss time that dump was started
+
+SLEEPING OPERATOR CONDITION: The problem is that SPXTAPE must have at
+least one drive at all times or else it terminates. Therefore, if the
+Operator goes to sleep, or to lunch, or to other duties, long enough
+for all mounted tapes to fill up, we must keep the last one active in
+a waiting-for-next-tape condition. When the operator returns and mounts
+any of the tapes whose mounts have been pending, we can then do the
+SPXTAPE END and DETACH commands for that drive.
+
+PREREQUISITES:
+WAKEUP - from the CMS Utilities/IPF set of tools.
+GETFMADR - from the CMS Utilities/IPF set of tools.
+External sorting program, such as PLSORT. Search on /LOCAL DEPEND/ to
+ find where you need to configure for your sort pgm.
+
+MODIFICATION HISTORY:
+05/13/96 - Roger Deschner - Handle tape drive that is attached after we
+ no longer need it. Don't issue SPXTAPE DUMP command at that
+ point, or a second dump may start itself up, and get all
+ confused.
+04/29/96 - Roger Deschner - When recovering from a sleeping-operator
+ condition, assume that the volume log has already arrived
+ by the time the operator wakes up and mounts the next tape.
+04/23/96 - Back up only specific files to tape during WRAPUP.
+04/22/96 - Roger Deschner - more bug fixes to detaching drive logic
+04/18/96 - Roger Deschner - fix bugs in reading logs, detaching drives
+04/17/96 - Roger Deschner - Change to a new method of waiting for the
+ log file to arrive in our reader to detach the drive. This may slow
+ the whole process down a little.
+04/15/96 - Roger Deschner - Change tape disposition back to RUN due to
+ not getting our logs on time at end of job. APAR opened with IBM
+ about rewind timing problem. Change to searching spool for logs.
+ (file BACKFILE LOGMSGS no longer needed?)
+04/12/96 - Roger Deschner - Changed tape disposition from RUN to REW to
+ help with tape-unloading timing problem. Also fix bug in
+ Mounted: so that arithmetic is OK after recovering from a DRAINING
+ drive if the Operator went to sleep.
+03/29/96 - Roger Deschner - Added CP REWIND to solve timing problems
+ wherein hardware errors would occur detaching a tape drive which was
+ fully unwound. VMTAPE would grab the drive and cause an error.
+03/21/96 - Roger Deschner - Original Version
+
+*/
+ADDRESS COMMAND
+/******************** begin configuration section *********************/
+/* Parameter string to be issued to SPXTAPE on initial invocation */
+/* spxparms = 'USER NOTISSRV STD ALL MODE NOCOMP RUN'/* debug trace */ */
+spxparms = 'SPOOL ALL MODE COMP RUN' /* Includes NSS, etc. */
+
+/* String of consecutive virtual addresses to use for tapes */
+/* Must be expressed as 4-digit addresses. */
+vdevs = '0181 0182 0183 0184 0185 0186 0187 0188 0189 018A 018B 018C'
+
+/* Userid of the VMTAPE service virtual */
+vmtapeid = 'VMTAPE'
+/********************* end configuration section **********************/
+
+/* Self-configuration section */
+/* Turn above list into stem */
+'PIPE LITERAL' vdevs,
+ '| SPLIT', /* Cut apart into individual tokens */
+ '| STEM vdev.'
+/* Who are we, and where are we? */
+'PIPE COMMAND IDENTIFY | VAR answer'
+PARSE VAR answer ourid . ournode .
+
+PARSE UPPER ARG action parms
+finalrc = 0
+SELECT
+ WHEN (action = 'START') THEN CALL START
+ WHEN (action = 'EOT') THEN CALL EOT /* called by BACKTRAP */
+ WHEN (action = 'UNLOADED') THEN CALL UNLOADED parms /* by BACKTRAP */
+ WHEN (action = 'MOUNTED') THEN CALL MOUNTED /* called by BACKMOUN */
+ WHEN (action = 'QUERY') THEN CALL QUERY
+ WHEN (action = 'DRIVES') THEN CALL DRIVES
+ WHEN (action = 'CANCEL') THEN CALL CANCEL
+ WHEN (action = 'NORMEND') THEN CALL NORMEND /* Called by BACKTRAP */
+ WHEN (action = 'WRAPUP') THEN CALL WRAPUP parms /*Called by ACV AFTER*/
+ WHEN (action = 'DUMP') THEN 'GLOBALV SELECT SPBACKUP LIST'
+ WHEN (action = 'DATA') THEN CALL DATA parms
+ OTHERWISE DO
+ SAY 'Invalid arguments to BACKUP.'
+ finalrc = 12
+ END
+END
+EXIT finalrc
+
+
+COMMONV: /* EXPOSE EVERYTHING */
+/**********************************************************************\
+* *
+* COMMONV Minor Subroutine *
+* *
+\**********************************************************************/
+/* GET or PUT everything we know about current status of the program. */
+PARSE UPPER ARG direction .
+IF (WORDPOS(direction,'GET PUT') < 1) THEN DO
+ SAY 'Internal error, COMMONV arg must be GET or PUT.'
+ EXIT 16
+END
+
+'GLOBALV SELECT SPBACKUP' direction 'DRIVEVOL.0' /* Stem drivevol. */
+DO i = 1 TO drivevol.0 /* Stem drivevol. */
+ 'GLOBALV SELECT SPBACKUP' direction 'DRIVEVOL.'i /* Stem drivevol. */
+END /* Stem drivevol. */
+'GLOBALV SELECT SPBACKUP' direction 'DRIVESTAT.0' /* Stem drivestat. */
+DO i = 1 TO drivestat.0 /* Stem drivestat. */
+ 'GLOBALV SELECT SPBACKUP' direction 'DRIVESTAT.'i /*Stem drivestat. */
+END /* Stem drivestat. */
+IF (direction = 'GET') THEN tapestat. = 'UNUSED'
+'GLOBALV SELECT SPBACKUP' direction 'TAPESTAT.0' /* Stem tapestat. */
+DO i = 1 TO tapestat.0 /* Stem tapestat. */
+ 'GLOBALV SELECT SPBACKUP' direction 'TAPESTAT.'i /* Stem tapestat. */
+END /* Stem tapestat. */
+'GLOBALV SELECT SPBACKUP' direction 'NDRIVES'
+'GLOBALV SELECT SPBACKUP' direction 'NSPXTAPE'
+'GLOBALV SELECT SPBACKUP' direction 'NINUSE'
+'GLOBALV SELECT SPBACKUP' direction 'TAPEVOLI'
+'GLOBALV SELECT SPBACKUP' direction 'QCOMPLETE'
+IF (direction = 'GET') THEN DO
+ 'GLOBALV SELECT SPBACKUP GET TAPEPOOL'
+ /* Get the list of tapes to use */
+ /* (Note that we reread the TAPEPOOL file here in case tapes were
+ added to the TAPEPOOL during the backup. This would be a thing to
+ do if a tapepool runs out partway through a backup. */
+ 'PIPE <' tapepool 'TAPEPOOL *',
+ '| NFIND *' ||, /* Eliminate comments */
+ '| SPECS 1-6 1', /* Just the volsers */
+ '| STEM tapevol.'
+ tapestat.0 = tapevol.0 /* (added tapes will automatically have value */
+ /* of UNUSED due to tapestat. = 'UNUSED') */
+END
+
+RETURN
+
+
+Start:
+/**********************************************************************\
+* *
+* START subroutine *
+* *
+\**********************************************************************/
+
+
+/* Initialize stems */
+drivevol. = ''
+drivevol.0 = vdev.0
+drivestat. = 'IDLE'
+drivestat.0 = vdev.0
+tapevoli = 1 /* We start this at 1 so that it uses tapevol.2 first. */
+ /* That is because tapevol.1 will contain the catalog. */
+ninuse = 0
+nspxtape = 0
+
+PARSE VAR parms tapepool ndrives .
+IF (ndrives = '') THEN ndrives = 2
+IF (tapepool = '') THEN DO
+ SAY 'BACKUP START Error: Tape pool name required.'
+ RETURN 16
+END
+
+/* Was a backup already started? */
+'GLOBALV SELECT SPBACKUP GET QBACKUP'
+IF (qbackup = 1) THEN DO
+ SAY 'Error: Backup is apparently already started.'
+ EXIT 12
+END
+qbackup = 1 /* It is now. */
+
+/* Prepare a clean slate of tape drives */
+DO i = 1 TO vdev.0
+ 'CP REWIND' vdev.i
+ 'PIPE CP DETACH' vdev.i '| HOLE'
+END
+'PIPE CMS VMTAPE CANCEL ALL | HOLE'
+
+/* VERY IMPORTANT - so we get the SPXTAPE logs: */
+'CP SPOOL CONS CLOSE' /* to whomever it was before */
+'CP SPOOL CONS TO *'
+'PIPE COMMAND ERASE BACKFILE SAVEMSGS A | HOLE'
+
+/* Who invoked us? Remember for future use. */
+'PIPE VAR runuser 1 | VAR runuser'
+'GLOBALV SELECT SPBACKUP PUT RUNUSER'
+'GLOBALV SELECT SPBACKUP PUT STARTUSER'
+
+/* Get the list of tapes to use */
+'PIPE <' tapepool 'TAPEPOOL *',
+ '| NFIND *' ||, /* Eliminate comments */
+ '| SPECS 1-6 1', /* Just the volsers */
+ '| STEM tapevol.'
+orc = rc
+IF ((orc <> 0) | (tapevol.0 < ndrives)) THEN DO
+ SAY 'Error: Tape pool' tapepool 'contains fewer tapes than number',
+ 'of drives specified.'
+ finalrc = 12
+ RETURN
+END
+
+/* Initialize stem. */
+tapestat. = 'UNUSED'
+tapestat.0 = tapevol.0
+tapestat.1 = 'RESERVED' /* Save first vol for restore catalog */
+
+/* Initialize qcomplete */
+qcomplete = 0
+
+/* Save everything we know */
+'GLOBALV SELECT SPBACKUP SETL QBACKUP 1'
+'GLOBALV SELECT SPBACKUP PUT TAPEPOOL'
+
+/* Mount the tapes */
+CALL REMOUNT
+
+/* Save everything else, including what REMOUNT might have changed */
+CALL COMMONV 'PUT'
+
+RETURN
+
+Remount: PROCEDURE EXPOSE vdev. drivevol. drivestat. ndrives ninuse,
+ tapevoli nspxtape tapestat.
+/**********************************************************************\
+* *
+* REMOUNT Minor Subroutine *
+* *
+\**********************************************************************/
+/* Issue VMTAPE MOUNT commands for these tapes. If any MOUNT*/
+/* commands fail, bypass and go to the next tape volume. */
+
+
+/* Get everything we need to know */
+'GLOBALV SELECT SPBACKUP GET TAPEPOOL'
+'GLOBALV SELECT SPBACKUP GET RUNUSER'
+/* Get the list of tapes to use */
+/* (Note that we reread the TAPEPOOL file here in case tapes were
+ added to the TAPEPOOL during the backup. This would be a thing to
+ do if a tapepool runs out partway through a backup. */
+'PIPE <' tapepool 'TAPEPOOL *',
+ '| NFIND *' ||, /* Eliminate comments */
+ '| SPECS 1-6 1', /* Just the volsers */
+ '| STEM tapevol.'
+tapestat.0 = tapevol.0 /* (added tapes will automatically have value */
+ /* of UNUSED due to tapestat. = 'UNUSED') */
+
+SAY 'REMOUNT called.' /* debug trace */
+SAY 'NDRIVES=' ndrives 'NINUSE=' ninuse, /* debug trace */
+ 'NSPXTAPE=' nspxtape 'TAPEVOLI=' tapevoli /* debug trace */
+SAY 'virt real tape '
+SAY 'addr addr status volser'
+SAY ' '
+DO i = 1 TO vdev.0
+ IF (WORDPOS(drivestat.i,'DUMPING DRAINING UNLOADING') > 0) THEN,
+ 'PIPE CP Q V' vdev.i,
+ '| SPECS 19.4 1',
+ '| VAR rdev'
+ ELSE rdev = '----'
+ SAY vdev.i ' ' rdev ' ' drivestat.i ' ' drivevol.i
+END
+
+DO FOREVER
+
+ /* Have we accomplished our mission? */
+ IF (ninuse >= ndrives) THEN LEAVE
+
+ /* Do we have any more tapes? */
+ tapevoli = 0
+ DO i = 1 TO tapestat.0
+ IF (tapestat.i = 'UNUSED') THEN DO
+ /* We have found what we were looking for */
+ tapevoli = i
+ LEAVE
+ END
+ END
+
+ IF (tapevoli = 0) THEN DO
+ ndrives = ninuse
+ /* We have run out of tapes. */
+ yak = 'CP MSGNOH' runuser
+ yak 'Not enough tapes in:' tapepool 'TAPEPOOL. You should add more.'
+ yak ' '
+ yak 'Until you do, we will continue running on only' ndrives,
+ 'drives.'
+ yak ' '
+ yak 'You should add more tapes now. Here is the procedure:'
+ yak ' '
+ yak 'New tapes must be in the VMTAPE catalog. (Use scratch tapes in',
+ 'emergency.)'
+ yak 'Get a MR link to' ourid '3E1, and XEDIT' tapepool,
+ 'TAPEPOOL.'
+ yak 'Add new volumes to the end of the file, file it, and release'
+ yak 'AND detach my minidisk. Then issue:'
+ yak ' '
+ yak 'SMSG' ourid 'ACCESS 3E1 C'
+ yak ' '
+ yak 'Then, to resume running on the full number of drives, issue:'
+ yak ' '
+ yak 'SMSG' ourid 'DRIVES n'
+ ITERATE
+ END
+
+ /* Find an unused vdev. Note range already checked elsewhere. */
+ DO i = 1 TO vdev.0
+ j = i
+ IF (drivestat.j = 'IDLE') THEN LEAVE
+ END
+
+ SAY 'Mounting' tapevol.tapevoli 'on vdev' vdev.j ' J='j
+ 'PIPE CMS VMTAPE MOUNT' tapevol.tapevoli vdev.j '(WRITE NOWAIT',
+ '| CONSOLE', /* DEBUG TRACE */
+ '| STEM errstem.'
+ orc = rc
+ IF (orc <> 0) THEN DO
+ 'PIPE LITERAL VMTAPE MOUNT failed. Possible TAPEPOOL error.',
+ '| APPEND STEM errstem.',
+ '| SPECS /MSGNOH' runuser'/ 1 1-* NEXTWORD',
+ '| CP',
+ '| CONSOLE' /* To see if any CP MSGs failed. */
+ END
+ drivevol.j = tapevol.tapevoli
+ drivestat.j = 'MOUNTING'
+ tapestat.tapevoli = 'MOUNTING'
+ ninuse = ninuse + 1
+END
+SAY 'exiting REMOUNT.' /* debug trace */
+SAY 'NDRIVES=' ndrives 'NINUSE=' ninuse, /* debug trace */
+ 'NSPXTAPE=' nspxtape 'TAPEVOLI=' tapevoli /* debug trace */
+SAY 'virt real tape '
+SAY 'addr addr status volser'
+SAY ' '
+DO i = 1 TO vdev.0
+ IF (WORDPOS(drivestat.i,'DUMPING DRAINING UNLOADING') > 0) THEN,
+ 'PIPE CP Q V' vdev.i,
+ '| SPECS 19.4 1',
+ '| VAR rdev'
+ ELSE rdev = '----'
+ SAY vdev.i ' ' rdev ' ' drivestat.i ' ' drivevol.i
+END
+RETURN
+
+
+Eot:
+/**********************************************************************\
+* *
+* EOT Major Subroutine *
+* *
+\**********************************************************************/
+/* Triggered by trapping an end-of-tape message from CP SPXTAPE. */
+/* In this routine, our runuser is ourself. (Should it be "SYSTEM"?) */
+
+/* Get all the information */
+CALL COMMONV 'GET'
+
+/* Which drive ran out? Why did it run out? */
+'PIPE (ENDCHAR ?) < BACKTRAP SAVEMSGS A',
+ '| CONSOLE',
+ '| A: TAKE FIRST 1',
+ '| VAR enddrive', /* now has the whole msg */
+ '| SPECS 14-* 1', /* Get what event this was */
+ '| SPECS WORDS 1-2 1', /* Just enough to tell it apart */
+ '| VAR endreason',
+ '?', /* This will only happen when ENDREASON=COMMAND */
+ 'A:', /* TIME STARTED: 02:01:48 */
+ '| FIND TIME STARTED:' ||,
+ '| SPECS WORDS 3 1',
+ '| SPECS 1-2 1 4-5 3 7-8 5', /* Remove ":" */
+ '| VAR starttime'
+
+SAY 'Entering EOT.' /* debug trace */
+SAY 'STARTTIME VARIABLE=' starttime
+SAY 'NDRIVES=' ndrives 'NINUSE=' ninuse, /* debug trace */
+ 'NSPXTAPE=' nspxtape /* debug trace */
+SAY 'ENDDRIVE=' enddrive /* debug trace */
+SAY 'ENDREASON=' endreason ' QCOMPLETE=' qcomplete
+
+SELECT
+ WHEN (SUBWORD(endreason,1,1) = 'END-OF-TAPE') THEN DO
+ enddrive = SUBSTR(enddrive,34,4)
+ /* Qiuck! Get rid of it! */
+ j = WORDPOS(enddrive,vdevs)
+
+ /* Mark this tape volume as having been used */
+ DO i = 1 TO tapevol.0
+ IF (tapevol.i = drivevol.j) THEN DO
+ tapestat.i = 'WRITTEN'
+ LEAVE
+ END
+ END
+
+ IF (nspxtape > 1) THEN DO /* Is the Operator asleep? */
+ /* Operator is not asleep. Behave normally. */
+ 'PIPE CP SPXTAPE END' enddrive /*'| HOLE'*/ '|console' /* debug */
+ /* Do NOT detach yet - wait until it unloads. */
+ ninuse = ninuse - 1
+ nspxtape = nspxtape - 1
+ /* Mark this vdev as still rewinding and unloading. It will be */
+ /* changed to IDLE when its Volume Log arrives in our reader. */
+ drivestat.j = 'UNLOADING'
+ drivevol.j = ''
+ END
+ ELSE DO
+ /* Operator is asleep. Can't unload this vdev or SPXTAPE ends. */
+ drivestat.j = 'DRAINING'
+ END
+
+ /* Mount next tape */
+ CALL REMOUNT
+ END
+ WHEN (SUBWORD(endreason,1,1) = 'COMPLETED') THEN DO
+ /* This means that we're done with this tape drive, and SPXTAPE */
+ /* does not want another tape. But it is still dumping on other */
+ /* drives, so don't close the show down yet. */
+ enddrive = SUBSTR(enddrive,33,4)
+ /* Qiuck! Get rid of it! */
+ j = WORDPOS(enddrive,vdevs)
+
+ /* The job is officially coming to its end */
+ IF (qcomplete = 0) THEN DO
+ qcomplete = 1
+ ADDRESS CMS 'VMTAPE CANCEL ALL'
+ END
+
+ /* Mark this tape volume as having been used */
+ DO i = 1 TO tapevol.0
+ IF (tapevol.i = drivevol.j) THEN DO
+ tapestat.i = 'WRITTEN'
+ LEAVE
+ END
+ END
+
+ IF (nspxtape > 1) THEN DO
+ /* We are not yet down to our last drive. */
+ 'PIPE CP SPXTAPE END' enddrive '| HOLE'
+ /* Do NOT detach yet - wait until it unloads. */
+ ninuse = ninuse - 1 /* Decrement what's being used */
+ nspxtape = nspxtape - 1
+ ndrives = ninuse /* Start shutting this thing down */
+ /* Mark this vdev as still rewinding and unloading. It will be */
+ /* changed to IDLE when its Volume Log arrives in our reader. */
+ drivestat.j = 'UNLOADING'
+ drivevol.j = ''
+ END
+ ELSE DO
+ /* We are down to our last drive, and SPXTAPE is pretty sure it
+ has enough room for all remaining files. */
+ drivestat.j = 'DRAINING'
+ END
+ END
+ WHEN (SUBWORD(endreason,1,1) = 'COMMAND') THEN DO
+ IF (SUBWORD(endreason,2,1) = 'ENDED') THEN DO /* Abnormal Ending! */
+ 'EXEC TELL' runuser 'SPBACKUP ended abnormally. Check the logs.'
+ /* Note abendmsg below is a nickname from SPBACKUP NAMES. */
+ 'EXEC EMAIL ABEND EMAIL * ( ABENDMSG' runuser '(NONOTEBOOK'
+ END
+ 'GLOBALV SELECT SPBACKUP PUT STARTTIME'
+ CALL NORMEND
+ END
+ OTHERWISE NOP
+END
+SAY 'Leaving EOT.' /* debug trace */
+SAY 'NDRIVES=' ndrives 'NINUSE=' ninuse, /* debug trace */
+ 'NSPXTAPE=' nspxtape /* debug trace */
+SAY 'virt real tape '
+SAY 'addr addr status volser'
+SAY ' '
+DO i = 1 TO vdev.0
+ IF (WORDPOS(drivestat.i,'DUMPING DRAINING UNLOADING') > 0) THEN,
+ 'PIPE CP Q V' vdev.i,
+ '| SPECS 19.4 1',
+ '| VAR rdev'
+ ELSE rdev = '----'
+ SAY vdev.i ' ' rdev ' ' drivestat.i ' ' drivevol.i
+END
+
+/* Save everything we know */
+CALL COMMONV 'PUT'
+
+RETURN
+
+
+Unloaded:
+/**********************************************************************\
+* *
+* UNLOADED major subroutine *
+* *
+\**********************************************************************/
+/* Triggered by the arrival of a spool file which we suspect is a
+SPXTAPE log file. We'll see if that is really the case... Note that if
+it is not a SPXTAPE Volume Log file, or if it is not one that WE want,
+then we do nothing. Anybody could send us a spool file for whatever
+reason pleases them, and we can't let that upset us. */
+
+/* Get all the information */
+CALL COMMONV 'GET'
+
+PARSE UPPER ARG spfn spft .
+IF (SUBSTR(spfn,5,4) <> 'DUMP') THEN DO /* Vol or CmdSum log? */
+ /* This is a Volume Log */
+ donevdev = SUBSTR(spfn,6,3)
+ found = 0
+ DO j = 1 TO vdev.0
+ IF (donevdev = vdev.j) THEN DO
+ found = 1
+ LEAVE
+ END
+ END
+ IF (found = 1) THEN DO
+ SELECT
+ WHEN (drivestat.j = 'UNLOADING') THEN DO
+ SAY 'Log file arrived for vdev' donevdev', and unload complete.'
+ 'CP DETACH' donevdev
+ drivestat.j = 'IDLE'
+ END
+ WHEN (drivestat.j = 'DRAINING') THEN DO /* Operator Asleep */
+ SAY 'Log file arrived for vdev' donevdev', but it cannot be',
+ 'unloaded because it is the last tape. Waiting for Operator.'
+ END
+ OTHERWISE NOP /* We REALLY don't want to do anything! */
+ END /* of SELECT */
+ END /* of IF (found = 1) THEN DO */
+ /* ELSE NOP - it's a Vol Log from some other SPXTAPE command. */
+END /* of /* This is a Volume Log */ */
+ELSE NOP /* Ignore Command Summary Log files */
+
+/* Save all the information */
+CALL COMMONV 'PUT'
+
+RETURN
+
+
+MOUNTED:
+/**********************************************************************\
+* *
+* MOUNTED Major Subroutine *
+* *
+\**********************************************************************/
+/* This is called when VMTAPE has completed a tape mount for us */
+/* BEWARE: In this routine, our "runuser" is actually VMTAPE. */
+
+/* Get all the information */
+CALL COMMONV 'GET'
+'GLOBALV SELECT SPBACKUP GET RUNUSER'
+
+SAY 'Entering MOUNTED:' /* debug trace */
+SAY 'NDRIVES=' ndrives 'NINUSE=' ninuse, /* debug trace */
+ 'NSPXTAPE=' nspxtape /* debug trace */
+SAY 'QCOMPLETE=' qcomplete
+
+IF (qcomplete = 1) THEN DO /* Do we still want this tape? */
+ SAY ' '
+ SAY 'A tape was attached to us after we no longer needed it. It will'
+ SAY 'be ignored, and will be detached as the backup job ends.'
+ SAY ' '
+END
+ELSE DO /* Yes, we want this tape. */
+ /* Which tape was mounted? Was there more than one? */
+ 'PIPE < BACKMOUN SAVEMSGS A',
+ '| FIND VMTMNT073I' ||,
+ '| STEM mounteds.',
+ '| CONSOLE' /* DEBUG TRACE */
+
+ DO i = 1 TO mounteds.0
+ PARSE VAR mounteds.i . 'Volume' mntvolser 'ready on' mntvdev .
+ mntvolser = STRIP(mntvolser,'B',"'") /* Remove quotes */
+ j = WORDPOS(mntvdev,vdevs) /* Which vdev is it? */
+ drivestat.j = 'DUMPING'
+
+ /* Mark this tape volume as being in active use */
+ DO i = 1 TO tapevol.0
+ IF (tapevol.i = drivevol.j) THEN DO
+ tapestat.i = 'WRITING'
+ LEAVE
+ END
+ END
+
+ /* Bump number of drives SPXTAPE is using */
+ nspxtape = nspxtape + 1
+
+ /* Is this the initial SPXTAPE command? */
+ IF (nspxtape = 1) THEN DO /* It is the first one */
+ 'PIPE CP SPXTAPE DUMP' vdev.j spxparms,
+ '| CONSOLE',
+ '| SPECS /MSGNOH' runuser'/ 1 1-* NEXTWORD',
+ '| CP',
+ '| CONSOLE'
+ END
+ ELSE DO /* It is a later one */
+ k = vdev.0
+ 'PIPE CP SPXTAPE DUMP' vdev.1 || '-' || vdev.k,
+ '| CONSOLE',
+ '| SPECS /MSGNOH' runuser'/ 1 1-* NEXTWORD',
+ '| CP',
+ '| CONSOLE'
+ END
+
+ /* If there are now exactly two drives, does this mean we can now */
+ /* safely get rid of a DRAINING drive? (i.e. Did a sleeping Operator*/
+ /* finally wake up and mount the next tape?) */
+ trace r
+ IF (nspxtape = 2) THEN DO
+ DO i = 1 TO drivevol.0
+ IF (drivestat.i = 'DRAINING') THEN DO
+ SAY 'Our wait is over.' vdev.i 'can now be SPXTAPE END-ed',
+ 'and DETACH-ed.'
+ 'PIPE CP SPXTAPE END' vdev.i '| HOLE'
+ /* Mark this tape volume as having been used */
+ DO k = 1 TO tapevol.0
+ IF (tapevol.k = drivevol.j) THEN DO
+ tapestat.k = 'WRITTEN'
+ LEAVE
+ END
+ END
+ /* We assume the Volume Log for this tape already arrived, */
+ /* while we were waiting for the sleeping operator to wake up. */
+ /* 'CP REWIND' vdev.i */ /* DO NOT REWIND or INT REQ happens! */
+ 'PIPE CP DETACH' vdev.i '| HOLE' /* Must detach */
+ drivestat.i = 'IDLE'
+ drivevol.i = ''
+ ninuse = ninuse - 1
+ nspxtape = 1
+ CALL REMOUNT
+ LEAVE /* Only do this ONCE! */
+ END
+ END
+ END
+ trace n
+ END
+END /* of ELSE DO /* Yes, we want this tape. */ */
+
+SAY 'leaving MOUNTED:' /* debug trace */
+SAY 'NDRIVES=' ndrives 'NINUSE=' ninuse, /* debug trace */
+ 'NSPXTAPE=' nspxtape /* debug trace */
+
+/* Save everything we know */
+CALL COMMONV 'PUT'
+
+RETURN
+
+
+DRIVES:
+/**********************************************************************\
+* *
+* DRIVES Major Subroutine *
+* *
+\**********************************************************************/
+/* The operator wishes to change the number of drives in use. */
+/* The runuser here may be any of the operators. Might not be the same
+one who started the backup. */
+
+PARSE VAR parms newndrives .
+
+/* Get all the information */
+CALL COMMONV 'GET'
+'GLOBALV SELECT SPBACKUP GET RUNUSER'
+
+oldndrives = ndrives
+SAY 'The number of drives was' oldndrives'.'
+
+sign = SUBSTR(newndrives,1,1)
+IF ((sign = '-') | (SIGN = '+')) THEN ndrives = ndrives + newndrives
+ ELSE ndrives = newndrives
+
+SAY 'Now it is set to' ndrives'.'
+
+'GLOBALV SELECT SPBACKUP PUT NDRIVES'
+
+SELECT
+ WHEN (ndrives > oldndrives) THEN DO
+ /* In case it was increased, mount more tapes. */
+ CALL REMOUNT
+ END
+ WHEN (ndrives < oldndrives) THEN DO
+ /* See if there are any MOUNTING drives we can take away */
+ DO i = 1 TO drivestat.0
+ IF (drivestat.i = 'MOUNTING') THEN DO
+ ADDRESS CMS 'VMTAPE CANCEL' vdev.i
+ /* Mark this tape volume as having NOT been used */
+ DO k = 1 TO tapevol.0
+ IF (tapevol.k = drivevol.i) THEN DO
+ tapestat.k = 'UNUSED'
+ LEAVE
+ END
+ END
+ /* Mark this vdev as being idle, with nothing mounted on it */
+ drivestat.i = 'IDLE'
+ drivevol.i = ''
+ ninuse = ninuse - 1
+ IF (ninuse <= ndrives) THEN LEAVE /* Accomplished our mission */
+ END
+ END
+ END
+ OTHERWISE NOP /* There was no change */
+END
+
+/* Save everything we know */
+CALL COMMONV 'PUT'
+
+RETURN
+
+
+QUERY:
+/**********************************************************************\
+* *
+* QUERY Major Subroutine *
+* *
+\**********************************************************************/
+/* The runuser here may be any of the operators. Might not be the same
+one who started the backup. */
+
+'GLOBALV SELECT SPBACKUP GET QBACKUP'
+IF (qbackup <> 1) THEN DO
+ SAY 'Backup job not active.'
+ RETURN
+END
+
+/* Get everything we need to know */
+CALL COMMONV 'GET'
+'GLOBALV SELECT SPBACKUP GET PROGRESS'
+'GLOBALV SELECT SPBACKUP GET RUNUSER'
+
+SAY 'NDRIVES=' ndrives 'NINUSE=' ninuse, /* debug trace */
+ 'NSPXTAPE=' nspxtape /* debug trace */
+SAY 'The backup was started by' runuser'.'
+SAY 'You have resuested for backup to run on' ndrives 'drives.'
+SAY 'Presently,' nspxtape 'are actually running,'
+SAY ' and we are waiting for you to mount' (ninuse-nspxtape) 'tapes.'
+SAY progress
+SAY ' '
+SAY 'virt real tape '
+SAY 'addr addr status volser'
+SAY ' '
+DO i = 1 TO vdev.0
+ IF (WORDPOS(drivestat.i,'DUMPING DRAINING UNLOADING') > 0) THEN,
+ 'PIPE CP Q V' vdev.i,
+ '| SPECS 19.4 1',
+ '| VAR rdev'
+ ELSE rdev = '----'
+ SAY vdev.i ' ' rdev ' ' drivestat.i ' ' drivevol.i
+END
+SAY ' '
+SAY 'Status of tapes in Tapepool' tapepool':'
+SAY ' '
+DO i = 1 TO tapevol.0
+ SAY RIGHT(i,2) tapevol.i tapestat.i
+END
+
+RETURN
+
+
+DATA:
+/**********************************************************************\
+* *
+* DATA Major Subroutine *
+* *
+\**********************************************************************/
+/* This subroutine is meant to be invoked by a program running in some
+user's virtual machine which wants to get information from us. It is
+returned by SMSG. */
+
+PARSE UPPER ARG varname .
+/* Who asked us this question? */
+'PIPE VAR runuser 1 | VAR runuser'
+'PIPE CMS GLOBALV SELECT SPBACKUP LIST' varname,
+ '| DROP FIRST 1',
+ '| APPEND LITERAL END OF RESPONSE',
+ '| SPECS /SMSG' runuser '/ 1 1-* NEXT',
+ '| CP'
+
+RETURN
+
+
+CANCEL:
+/**********************************************************************\
+* *
+* CANCEL Major Subroutine *
+* *
+\**********************************************************************/
+/* End the whole show, right now. */
+/* The runuser here may be any of the operators. Might not be the same
+one who started the backup. */
+
+DO i = 1 TO vdev.0
+ 'CP SPXTAPE CANCEL' vdev.i
+ 'CP REWIND' vdev.i
+ 'CP DETACH' vdev.i
+END
+'GLOBALV SELECT SPBACKUP SETL QBACKUP 0'
+ADDRESS CMS 'VMTAPE CANCEL ALL'
+finalrc = 0
+RETURN
+
+
+NORMEND:
+/**********************************************************************\
+* *
+* NORMEND Major Subroutine *
+* *
+\**********************************************************************/
+/* Called from a TRAP when SPXTAPE reaches its normal end */
+/* We are the runuser here ourselves. */
+
+/* Get all information */
+CALL COMMONV 'GET'
+'GLOBALV SELECT SPBACKUP GET RUNUSER'
+'GLOBALV SELECT SPBACKUP GET STARTTIME'
+
+/* Unload all the hardware */
+ADDRESS CMS 'VMTAPE CANCEL ALL'
+DO i = 1 TO vdev.0
+ 'PIPE CP DETACH' vdev.i '| HOLE'
+END
+
+yak = 'CP MSGNOH' runuser
+yak ' '
+yak 'BACKUP has finished writing the spool files to tape. Now we must'
+yak 'write the logs of this backup to tape. This will take about 10'
+yak 'minutes.'
+yak ' '
+/* (Delay unnecessary here. All logs have already arrived.) */
+/* 'PIPE LITERAL +2:00', */
+/* '| DELAY', */
+/* '| SPECS /We have waited / 1 1-* NEXT', */
+/* '| CONSOLE' */
+
+'PIPE < BACKTRAP SAVEMSGS A | CONSOLE'
+
+/* Prepare log file */
+logfid = tapepool 'TAPELOG A'
+'PIPE COMMAND ERASE' logfid '| HOLE'
+'RENAME BACKTRAP SAVEMSGS A' logfid
+'PIPE LITERAL ',
+ '| APPEND LITERAL Tapepool status at end of job:',
+ '| >>' logfid
+DO i = 1 TO tapevol.0
+ 'PIPE LITERAL' RIGHT(i,2) RIGHT(tapevol.i,6) tapestat.i,
+ '| >>' logfid
+END
+/* Tell the guy who started the whole show */
+'PIPE <' logfid,
+ '| SPECS /MSGNOH' runuser'/ 1 1-* NEXTWORD',
+ '| CONSOLE',
+ '| CP'
+
+CALL COMMONV 'PUT'
+
+/* Wrap it up. */
+
+CALL WRAPUP starttime tapepool
+RETURN result
+
+Wrapup:
+/**********************************************************************\
+* *
+* WRAPUP Major Subroutine *
+* *
+\**********************************************************************/
+/* Post-process the logs */
+
+/* Get all information */
+/* CALL COMMONV 'GET' */ /* DON'T re-get - we may be called standalone*/
+'GLOBALV SELECT SPBACKUP GET RUNUSER'
+PARSE ARG starttime tapepool .
+IF ((starttime = '') | (tapepool = '')) THEN DO
+ message = 'ARGUMENTS MISSING ON WRAPUP - NEED startime tapepool'
+ SAY message
+ RETURN 40
+END
+
+'PIPE CP 500000 QUERY RDR * ALL', /* Look at all our RDR files */
+ '| LOCATE 1-8 /' || ourid || '/', /* That came from me */
+ '| LOCATE 15-19 /T CON/', /* That look like SPBACKUP files */
+ /* Note we include both Volume Logs and Command Summary Logs below*/,
+ '| LOCATE 58 /D/', /* That are Dump jobs */
+ '| LOCATE 64-69 /' || starttime || '/', /* That are this job */
+ '| SPECS 10-13 1', /* and just save the spids */
+ '| STEM spids.'
+orc = rc
+IF (orc <> 0) THEN DO
+ SAY 'Error encountered locating logs in reader. RC=' orc
+ RETURN orc
+END
+
+/* Read in the log files one at a time */
+SAY 'Reading console logs...'
+'PIPE COMMAND ERASE' tapepool 'UNSORTED A | HOLE'
+DO i = 1 TO spids.0
+/* A sample:
+ORIGINID FILE CLASS RECORDS CPY HOLD DATE TIME NAME TYPE DIST
+U12860 2537 T CON 00000033 001 NONE 01/25 13:01:28 0125DUMP 130128 UICVM
+U12860 2536 T CON 00000020 001 NONE 01/25 13:19:28 0125D181 13012803 UICVM
+*/
+
+ /* Get complete info on this spool file */
+ 'PIPE CP QUERY RDR *' spids.i 'ALL',
+ '| TAKE LAST 1',
+ '| VAR spooldef'
+ PARSE VAR spooldef originid . class variety . . . . . spfn spft .
+ SAY 'Examining file' spid spfn spft
+ SELECT
+ WHEN (originid <> ourid) THEN NOP /* Ignore not from us */
+ WHEN (variety <> 'CON') THEN NOP /* Ignore not CON file */
+ WHEN (SUBSTR(spfn,5,4) = 'DUMP') THEN DO /* the main log */
+ SAY ' Receiving Command Summary Log' spid spfn spft
+ ADDRESS CMS 'RECEIVE' spids.i tapepool 'SPXLOG A (REPLACE'
+ END
+ OTHERWISE DO /* Read this part in */
+ SAY ' Receiving Volume Log' spid spfn spft
+ /* Spool rdr HOLD so we can read it again */
+ 'CP SPOOL RDR NOCONT NOEOF NOKEEP CLASS * HOLD'
+ 'PIPE (ENDCHAR ?) READER FILE' spids.i, /* Read from reader */
+ '| SPECS 2-* 1', /* Get rid of carriage control */
+ '| TOLABEL USERID FILE QUEUE' ||, /* Up until detail list*/
+ '| BANANA: NFIND TAPE NUMBER:' ||, /* ldev, seq */
+ '| FIND Tape Volume ID:' ||, /* Get SL volser */
+ '| SPECS 18.6 1',
+ '| VAR slvol', /* and save it */
+ '?',
+ 'BANANA:',
+ '| SPECS 22-* 1',
+ '| VAR tapenum' /* Save ldev, seq */
+ 'CP CLOSE READER' /* Rewind in order to reread */
+ 'CP SPOOL RDR NOCONT NOEOF NOKEEP CLASS * NOHOLD' /* NOHOLD */
+ 'PIPE READER FILE' spids.i, /* Read the real stuff this time */
+ '| SPECS 2-* 1', /* Get rid of carriage control */
+ '| FRLABEL USERID FILE QUEUE' ||, /* Now, start at detail */
+ '| DROP FIRST 1', /* The header line itself. */
+ '| SPECS /' || LEFT(slvol,6) || '/ 1', /* Spread the volser...*/
+ '/' || LEFT(tapenum,8) || '/ 8', /*...and tapeseq...*/
+ '1-* 17', /*...across all the detail records. */
+ '| >>' tapepool 'UNSORTED A' /* Write to raw database */
+ END
+ END
+END
+
+/* Sort the records */
+SAY 'Sorting database...'
+/* Build SORT control statements to
+and put everything in order BY userid, spoolnum. */
+/* Note that these starting locations are +4 for green words */
+'PIPE LITERAL SORT FIELDS=(21,13,CH,A)',
+ '| PAD 80', /* PLSORT requires F 80 records */
+ '| > SPBACKUP SORTCNTL A F 80'
+
+infid = tapepool 'UNSORTED A'
+outfid = tapepool 'DATABASE A'
+'PIPE CMS ERASE' outfid '| HOLE'
+/************BEGIN LOCAL DEPENDENT CODE *****************************/
+/* Get access to the external sort, and sort the file */
+'EXEC GETDISK SORT'
+ADDRESS CMS 'PLSORT' infid outfid 'SPBACKUP SORTCNTL A'
+'EXEC GETDISK SORT (FREE'
+/************ END LOCAL DEPENDENT CODE *****************************/
+
+/* We're done with the unsorted version, and it's pretty big. */
+'ERASE' tapepool 'UNSORTED A'
+
+/* Build the database index file */
+SAY 'Building database index...'
+'PIPE <' tapepool 'DATABASE A',
+ '| SPECS 17.8 1 RECNO 10',
+ '| UNIQUE 1-8 FIRST',
+ '| >' tapepool 'DBINDEX A'
+
+/* TAPE DUMP today's logs to tape. */
+SAY 'Writing database to tape...'
+/* Mount the first tape */
+'GLOBALV SELECT SPBACKUP GET TAPEPOOL'
+/* Get the list of tapes to use */
+'PIPE <' tapepool 'TAPEPOOL *',
+ '| NFIND *' ||, /* Eliminate comments */
+ '| SPECS 1-6 1', /* Just the volsers */
+ '| STEM tapevol.'
+'PIPE CMS VMTAPE MOUNT' tapevol.1 181 '(WRITE WAIT',
+ '| STEM errstem.'
+orc = rc
+IF (orc <> 0) THEN DO
+ SAY 'Error occured trying to mount tape. TAPEPOOL error?'
+ 'PIPE STEM errstem. | CONSOLE'
+ finalrc = orc
+ RETURN
+END
+
+/* Copy everything relevant to tape. */
+'TAPE REW'
+'PIPE TAPE | CONSOLE' /* Skip over, and display, standard label */
+'TAPE REW'
+'TAPE FSF 1'
+'TAPE DUMP' tapepool 'SPXLOG A (COMP BLKSIZE 64K'
+'TAPE DUMP' tapepool 'TAPELOG A (COMP BLKSIZE 64K'
+'TAPE DUMP' tapepool 'DBINDEX A (COMP BLKSIZE 64K'
+'TAPE DUMP' tapepool 'DATABASE A (COMP BLKSIZE 64K'
+'TAPE WTM 2'
+'TAPE REW'
+'CP DETACH 181'
+
+'GLOBALV SELECT SPBACKUP SETL QBACKUP 0'
+SAY 'Backup complete.'
+yak = 'CP MSGNOH' runuser
+yak ' '
+yak 'BACKUP has finished writing the restore catalog to tape. Spool'
+yak 'backup is now complete. Thank you.'
+yak ' '
+RETURN rc
diff --git a/vmworkshop-vmarcs/1996/spbackup/email.exec b/vmworkshop-vmarcs/1996/spbackup/email.exec
new file mode 100644
index 0000000..c0f7a9c
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/email.exec
@@ -0,0 +1,83 @@
+/* Send a message as e-mail. The message is contained in the CMS file
+whose name is passed as this program's arguments. The message will be
+scanned for the change strings passed in the stack.
+
+SYNTAX:
+
+EXEC EMAIL fn ft fm (JOHNDOE (NONOTEBOOK
+ ||||||| ||||||||||
+ Recipient(s) NOTE cmd options
+
+FORMAT OF STACKED LINES:
+
+/&NAME/John Doe/
+/&SVMNAME/RSCS/
+!&SCP!VM/ESA!
+
+The message file should begin with the "Subject:" line, followed by a
+single blank line, followed by the body of the message starting on line 3
+
+When creating test and changes, be careful not to extend lines too long.
+
+THIS PROGRAM EXPLOITS THE STACK. It expects data for it to be on the
+stack, and then it uses the stack internally. Calling programs should
+not depend upon the state of the stack before or after this program.
+
+MODIFICATION HISTORY:
+02/17/95 - Roger Deschner - Original version
+*/
+PARSE UPPER ARG msgfn msgft msgfm '(' noteopts
+/* Are we being called as an EXEC or as an XEDIT Profile? */
+IF (msgfn = ')PROFILE') THEN SIGNAL PROFILE
+
+/* Mainline code */
+ADDRESS COMMAND
+
+IF (msgfm = '') THEN msgfm = '*'
+ADDRESS CMS 'ESTATE' msgfn msgft msgfm
+IF (rc <> 0) THEN EXIT rc
+
+/* Pull changes off the stack; write pipeline stages. */
+IF (QUEUED() > 0) THEN DO
+ 'PIPE (STAGESEP \) STACK',
+ '\ SPECS /| CHANGE/ 1 1-* NEXTWORD',
+ '\ JOIN *',
+ '\ VAR changes'
+END
+ELSE DO
+ changes = ''
+END
+
+
+/* Read in the file and change it. */
+
+QUEUE 'EXEC EMAIL )PROFILE'
+'PIPE <' msgfn msgft msgfm,
+ changes,
+ '| SPECS /I / 1 1-* NEXT', /* Make into XEDIT INPUT commands */
+ '| PAD 3', /* Minimum length for I command */
+ '| STACK'
+QUEUE 'SEND'
+'EXEC NOTE' noteopts
+EXIT
+
+
+/**********************************************************************\
+* THIS IS AN XEDIT PROFILE: *
+\**********************************************************************/
+PROFILE:
+ADDRESS XEDIT
+'SET CASE M I'
+'EXTRACT /MSGMODE'
+'SET MSGMODE OFF'
+/* Get rid of Subject line and blank line after it, if present. */
+'TOP'
+'FIND Subject:'
+orc = rc
+IF (orc = 0) THEN 'DELETE *'
+/* Last line blank? */
+'BOTTOM'
+'EXTRACT /CURLINE'
+IF (curline.3 = ' ') THEN 'DELETE 1'
+'SET MSGMODE' msgmode.1 msgmode.2
+EXIT
diff --git a/vmworkshop-vmarcs/1996/spbackup/fri.tapepool b/vmworkshop-vmarcs/1996/spbackup/fri.tapepool
new file mode 100644
index 0000000..b4a9802
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/fri.tapepool
@@ -0,0 +1,21 @@
+* This is a SPBACKUP tapepool for Friday morning jobs
+RDFR00
+RDFR01
+RDFR02
+RDFR03
+RDFR04
+RDFR05
+RDFR06
+RDFR07
+RDFR08
+RDFR09
+RDFR10
+RDFR11
+RDFR12
+RDFR13
+RDFR14
+RDFR15
+RDFR16
+RDFR17
+RDFR18
+RDFR19
diff --git a/vmworkshop-vmarcs/1996/spbackup/install.memo b/vmworkshop-vmarcs/1996/spbackup/install.memo
new file mode 100644
index 0000000..1e72ee3
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/install.memo
@@ -0,0 +1,94 @@
+ S P B A C K U P
+ INSTALLATION INSTRUCTIONS
+
+ Spool Backup System, using SPXTAPE
+ University of Illinois at Chicago
+ June 22, 1996
+
+
+SPBACKUP is a spool backup system which uses the new SPXTAPE command to
+back up the spool. It's purpose is to create an interface between the CP
+SPXTAPE command, and the VMTAPE tape system, and to create catalogs and
+indexes to facilitate restores.
+
+Prerequisites:
+
+ 1) VM/ESA Version 2 (2.1.0+) (See below if earlier VM)
+ 2) CMS/IPF Utilities Set
+
+To install SPBACKUP:
+
+Create a directory entry for it, similar to this:
+
+ USER SPBACKUP password 5M 8M ABDGM 64 OFF OFF 16 OFF
+ INCLUDE SYSACCTM <-- usual for your installation
+ IPL CMS
+ OPTION MAXCONN 255
+ IUCV ALLOW PRIORITY MSGLIMIT 255
+ * 191: Work files, scratch files, RESTORE CATALOGS
+ MDISK 191 3390 1218 400 VM0935 MR
+ * 3E1: SVM's Executable code, configuration files, TAPEPOOL DEFINITIONS
+ * Note this is an RR link so you can get a M link and change stuff
+ MDISK 3E1 3390 1 1 VM0920 RR
+
+The size of the 191 minidisk will be determined by how many spool files
+you will typically have in your system, and how many backup cycles you
+are going to keep. The 400 cylinder size is about right for 200,000 spool
+files, and 8 cycles (7 days plus one for emergency backups.)
+
+Insure that the 3E1 minidisk has access mode RR in the directory! This is
+to allow you to reconfigure the server on the fly.
+
+Load all files onto the 3E1 minidisk, except for file PROFILE EXEC which
+must be on the 191 minidisk, and file SPBACKUP EXEC which must be placed
+on some minidisk that all Operators and Systems Programmers have access
+to. End-users need not have access to SPBACKUP EXEC, but it won't hurt if
+they do, as they are prevented from doing anything if they are not listed
+in the CONFIG file. (below). Therefore, you could place it on your Y disk
+if you have no other, more private, place.
+
+Edit file SPBACKUP CONFIG to define the list of users authorized to use
+the program.
+
+If your Sterling VM:Tape Service Virtual is not named VMTAPE, you will
+need to review all EXEC files to change it. Search for the string
+CONFIGURATION SECTION in each one. It needs to be changed in only one
+place in each EXEC file.
+
+Insure that the CMS Utilities are available to the SPBACKUP service
+virtual. This may involve a directory LINK statement.
+
+Insure that an external file sorting program is available to the program,
+such as PLSort or SyncSort. Edit file BACKUP EXEC, and do a string search
+on LOCAL DEPEND to find where to modify the program to get access to your
+sorting program and run it. If your sorting program can benefit from
+additional storage, do it.
+
+If your tape drives do not support hardware compression, edit file BACKUP
+EXEC, search for its CONFIGURATION SECTION, and edit the SPXTAPE options
+string to remove the keyword COMP. Enter HELP SPXTAPE DUMP for details
+about the COMP option. If in doubt about your tape hardware, try it first
+with COMP; it really helps.
+
+If you are running a version of VM before ESA 2.1.0, you will need to
+modify the code in BACKUP EXEC to trap the volser of the tape at the
+point at which the Volume Log is returned. This may not be a trivial mod;
+I didn't do it because I didn't need to.
+
+Edit the TAPEPOOL files, to contain the volsers of the sets of tapes you
+want to use. Insure that those tapes are in the VMTAPE catalog, and that
+the tapes actually exist in your rack, and are initialized with matching
+OS Standard Labels. (Use TAPE WVOL1.)
+
+Compiling the code with the Rexx compiler will only help slightly. The
+greatest performance gains can be achieved by experimenting with more or
+fewer tape drives, and by training your operators to respond quickly to
+VMTAPE tape mount requests. (See SYSOPTAP for something which may help
+here.)
+
+Distribute copies of SPBACKUP OPERATE to your operators.
+
+SPBACKUP was written by:
+
+Roger Deschner University of Illinois at Chicago rogerd@uic.edu
+Computer Center M/C 135, 1940 W. Taylor St., Room 124, Chicago, IL 60612
diff --git a/vmworkshop-vmarcs/1996/spbackup/ipl.tapepool b/vmworkshop-vmarcs/1996/spbackup/ipl.tapepool
new file mode 100644
index 0000000..d2ac2f0
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/ipl.tapepool
@@ -0,0 +1,21 @@
+* This is a SPBACKUP tapepool for test and special use
+RDIP00
+RDIP01
+RDIP02
+RDIP03
+RDIP04
+RDIP05
+RDIP06
+RDIP07
+RDIP08
+RDIP09
+RDIP10
+RDIP11
+RDIP12
+RDIP13
+RDIP14
+RDIP15
+RDIP16
+RDIP17
+RDIP18
+RDIP19
diff --git a/vmworkshop-vmarcs/1996/spbackup/mon.tapepool b/vmworkshop-vmarcs/1996/spbackup/mon.tapepool
new file mode 100644
index 0000000..9ebb29a
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/mon.tapepool
@@ -0,0 +1,21 @@
+* THIS IS A SPBACKUP TAPEPOOL FOR MONDAY MORNING JOBS
+RDMO00
+RDMO01
+RDMO02
+RDMO03
+RDMO04
+RDMO05
+RDMO06
+RDMO07
+RDMO08
+RDMO09
+RDMO10
+RDMO11
+RDMO12
+RDMO13
+RDMO14
+RDMO15
+RDMO16
+RDMO17
+RDMO18
+RDMO19
diff --git a/vmworkshop-vmarcs/1996/spbackup/operate.memo b/vmworkshop-vmarcs/1996/spbackup/operate.memo
new file mode 100644
index 0000000..b2dd294
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/operate.memo
@@ -0,0 +1,115 @@
+ S P B A C K U P
+
+ OPERATING INSTRUCTIONS
+ April 10, 1996
+
+
+SPBACKUP is a spool backup system which uses the new SPXTAPE command to
+back up the spool. It's purpose is to create an interface between the CP
+SPXTAPE command, and the VMTAPE tape system, and to create catalogs and
+indexes to facilitate restores. It replaces the old SPTAPE command.
+
+SPBACKUP is a service virtual which is always logged on. You communicate
+with it by the SPBACKUP exec, SPBACKUP EXEC prompts you for all the
+information it needs, and then handles communications with the SPBACKUP
+service virtual.
+
+It will send information about its progress to the userid who issued the
+command to start it, however any authorized user may use the Query option
+to request progress information.
+
+EXAMPLE:
+
+R; spbackup
+ SPBACKUP Spool Backup System
+
+ Type a letter and press ENTER:
+
+ Q QUERY current status of backup jobs
+ S START a backup job
+ D Change number of DRIVES currently in use for backup.
+ C CANCEL currently running backup job.
+
+ ...or just press enter without typing anything to leave.
+
+The Q option will tell you if a spool backup job is currently running,
+and if it is, which tapes are mounted on which drives, and how far along
+(in %done) the job is.
+
+The S option will start a spool backup job. Once a SPBACKUP backup is
+started, it will not allow a second one to be started. The S option will
+issue prompts for tapepool name, and for the number of drives you wish to
+use.
+
+START BACKUP JOB EXAMPLE:
+
+ Enter tapepool name: SUN MON TUE WED THU FRI SAT IPL,
+ or press Enter for default MON -
+
+ Enter number drives, from 2 to 12, or just press Enter to quit.
+ (3 to 5 drives usually provides best performance.)
+ 4
+
+ You have requested to start a spool backup using Tape Pool IPL
+ on 5 tape drives. This will overwrite and erase the previous
+ backup which was written on Tape Pool IPL. This backup will take
+ from 1.5 to 3 hours to run. Type "Y" to confirm and start this backup
+ or type anything else to quit now without starting anything.
+
+ Start the backup now? (Y or N) -
+ y
+
+There are Tape Pools corresponding to each day of the week, plus one more
+for special backups called IPL.
+
+Tape pools are defined on minidisk SPBACKUP 3E1, which you can get a
+WRITE link to and edit to add tapes, etc. If you change something on
+disk 3E1, the command SMSG SPBACKUP RLDDATA will make the server reaccess
+its read-only link to this disk, so it will see your changes.
+
+SPBACKUP requires a minimum of two tape drives. This is so that the
+backup can continue while you are mounting the next tape, after one tape
+is filled. Of course, it runs faster with more drives - up to a point.
+Early tests indicate that using more than 6 drives does not provide any
+additional speed. Using fewer drives will result in using fewer total
+tapes, because there will be fewer unfilled tapes when the job completes.
+(Therefore, you may wish to reduce the number of drives to 2 as a job
+nears completion, to minimize the number of unfilled tapes, but this is
+not essential.)
+
+New tapes will be mounted one at a time as the previous ones are filled.
+There is no need to reserve particular drives; VMTAPE is in full control
+of the drives at all times, and will assign them to SPBACKUP just like to
+any other user.
+
+You can start with any number of drives, and change this upwards and
+downwards in the middle of the backup job, using the D (drives) option.
+
+If this is more than were being used before, it will immediately start
+those drives and issue VMTAPE mounts for tapes to use on those new
+drives. If this is fewer than were being used before, it will wait until
+the next tape fills up, and then simply not mount another tape to replace
+the one which filled up, until it gets down to the number of drives you
+specified. (Minimum two drives.)
+
+SPBACKUP needs to use a minimum of two tape drives; it cannot work with
+just one drive.
+
+The "C" (CANCEL) option will cancel an in-progress backup job before it
+is done. IF YOU CANCEL, YOU WILL NEED TO RESTART THIS BACKUP FROM THE
+BEGINNING.
+
+POST-PROCESSING: After the backup is complete, the monitor svm will
+process the SPXTAPE log files into a database which will enable us to
+restore easily, and then write that database to one of the tape pool
+tapes with TAPE DUMP. While this post-processing is underway (about 10
+minutes) the service virtual will consume a lot of CPU time, and will
+not respond to queries. When it finishes building the restore catalog
+database and index, it will request one tape mount so it can back them
+up too.
+
+RESTORES: At this point, restores must be handled manually by the
+Systems Programming Staff.
+
+AUTHORIZATION: The list of users authorized to communicate with
+SPBACKUP is contained in file SPBACKUP CONFIG on minidisk SPBACKUP 3E1.
diff --git a/vmworkshop-vmarcs/1996/spbackup/process.exec b/vmworkshop-vmarcs/1996/spbackup/process.exec
new file mode 100644
index 0000000..d470c38
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/process.exec
@@ -0,0 +1,163 @@
+/* PROCESS EXEC for SPBACKUP:
+
+Wait for IUCV's mostly, and occasionally set a timer in case something
+needs to be waited for.
+
+This routine saves NOTHING in its ownm Rexx variable pool. All
+information intended to be saved should be stored in GLOBALV.
+
+MODIFICATION HISTORY:
+04/04/96 - Roger - Add RESTORE, DATA functions
+02/13/96 - Roger - Original version (stolen from ZPRINTER)
+
+Wakeup Return Codes:
+ -2 First time invocation from CMS subset (WAKEUP cannot be
+ invoked from CMS subset)
+ 0 Nothing to wait for--all timer file options are done
+ 1 VMCF/SMSG received and stacked
+ 2 The specified time period has elapsed
+ 3 A time from the timer file has been reached
+ 4 A reader file of the proper class has arrived
+ 5 A message has arrived
+ 6 Interrupt from the virtual console
+ 7 I/O interrupt
+ 8 External interrupt
+ 100 Explanation complete (when '?' specified)
+ 101 Empty file in card reader, RDR option (RC from FSREAD)
+ 103 Unknown error from card reader, RDR option (RC from FSREAD)
+*/
+ADDRESS COMMAND
+
+/*************** BEGIN CONFIGURATION SECTION **************************/
+/* Userid of the VMTAPE service virtual */
+vmtapeid = 'VMTAPE'
+/***************** END CONFIGURATION SECTION **************************/
+
+/* Read configuration information */
+'PIPE < SPBACKUP CONFIG *', /* Authorized userids */
+ '| NFIND *' ||, /* Eliminate comment lines */
+ '| SPECS WORDS 1 1', /* Just take first token */
+ '| XLATE UPPER', /* Fold to uppercase */
+ '| JOIN * / /', /* Make into one big string */
+ '| VAR authusers'
+
+ /* WAKEUP when the first of these occurs:
+ o A console interrupt occurs
+ o A spool file arrives containing a restore job
+ o An IUCV or SMSG interrupt occurs, which could be any of:
+ *MSG - CP MSG or MSGNOH command
+ *WNG - CP WNG command
+ *EMSG - SET EMSG IUCV
+ *IMSG - SET IMSG IUCV
+ *SMSG - SET SMSG IUCV
+ *CP - SET CPCONIO IUCV
+ *VM - SET VMCONIO IUCV
+ */
+'CP SET MSG IUCV'
+'CP SET SMSG IUCV'
+'CP SET CPCONIO IUCV'
+DO FOREVER
+ SAY DATE('USA') TIME() 'At your service.'
+ 'WAKEUP (CONS IUCVMSG'
+ wakerc = rc
+ /* OK, who woke us up? What happened? Why? What's going on? */
+ SELECT
+ WHEN ((wakerc = 1) | (wakerc = 5)) THEN DO
+ PARSE PULL msgtype runuser textline
+ SELECT
+ WHEN (msgtype = '*CP') THEN DO
+ SAY textline
+ ADDRESS COMMAND 'EXEC BACKTRAP' textline
+ orc = rc
+ END
+ WHEN ((msgtype = '*MSG') | (msgtype = '*SMSG')) THEN DO
+ IF (runuser = vmtapeid) THEN DO
+ SAY textline
+ ADDRESS COMMAND 'EXEC BACKMOUN' textline
+ END
+ ELSE DO
+ SAY textline
+ /* Somebody is trying to tell us something */
+ PARSE VAR textline action parms
+ UPPER action
+
+ /* Check for shortcut commands */
+ IF (WORDPOS(action,'START QUERY DRIVES CANCEL DATA') > 0),
+ THEN DO
+ parms = textline
+ action = 'BACKUP'
+ END
+
+ CALL SECURITY runuser action parms
+ IF (result <> 0) THEN DO
+ 'EXEC TELL' runuser 'Unauthorized command.'
+ END
+ ELSE DO /* This guy's OK. Do it. */
+ SELECT
+ WHEN (action = 'BACKUP') THEN DO
+ 'PIPE COMMAND EXEC BACKUP' parms,
+ '| CONSOLE',
+ '| SPECS \CP MSGNOH' runuser '\ 1 1-* NEXT',
+ '| COMMAND',
+ '| CONSOLE'
+ finalrc = rc
+ END
+ WHEN (action = 'RESTORE') THEN DO
+ 'PIPE COMMAND EXEC RESTORER' parms,
+ '| CONSOLE',
+ '| SPECS \CP MSGNOH' runuser '\ 1 1-* NEXT',
+ '| COMMAND',
+ '| CONSOLE'
+ finalrc = rc
+ END
+ WHEN (action = 'RLDDATA') THEN DO
+ 'CP SET MSG ON'
+ 'CP SET SMSG ON'
+ 'CP SET CPCONIO OFF'
+ CALL VERYLAST
+ 'PIPE COMMAND ACCESS 3E1 C',
+ '| LITERAL' userid() 'has executed:',
+ '| CONSOLE',
+ '| SPECS /CP MSGNOH' runuser '/ 1 1-* NEXT',
+ '| CP',
+ '| CONSOLE'
+ 'CONWAIT'
+ 'DESBUF'
+ QUEUE 'EXEC PROCESS'
+ CALL EXIT 0
+ END
+ OTHERWISE DO
+ 'EXEC TELL' runuser 'Unknown command:' action
+ END
+ END
+ END
+ END
+ END
+ OTHERWISE DO
+ SAY 'Unknown activity.'
+ END
+ END
+ END
+ OTHERWISE DO /* If something funny happened, leave.*/
+ SAY 'Terminating due to console interrupt.',
+ 'Enter PROCESS to resume. WAKEUP rc' wakerc
+ /* 'WAKEUP RESET' */ /* turn messages from IUCV to ON */
+ CALL EXIT 40
+ END
+ END /* End of SELECT on type of WAKEUP interrupt */
+END
+
+EXIT:
+PARSE ARG finalrc .
+'CP SET MSG ON'
+'CP SET SMSG ON'
+'CP SET CPCONIO OFF'
+EXIT finalrc
+
+SECURITY: PROCEDURE EXPOSE authusers
+PARSE UPPER ARG runuser action .
+IF (WORDPOS(runuser,authusers) > 0) THEN ok = 0; ELSE ok = 4
+RETURN ok
+
+/* To insure this entire program is in core when doing ACCESS */
+VERYLAST: RETURN
diff --git a/vmworkshop-vmarcs/1996/spbackup/profile.exec b/vmworkshop-vmarcs/1996/spbackup/profile.exec
new file mode 100644
index 0000000..81f68d1
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/profile.exec
@@ -0,0 +1,28 @@
+/* PROFILE EXEC for the SPBACKUP virtual machine. Place this on 191-A.
+All other files go on the 3E1 minidisk.
+See file PROCESS EXEC on minidisk 3E1 for everything else.
+
+MODIFICATION HISTORY:
+04/19/96 - Roger Deschner - Add TERMINAL LINEND # to allow #CP DISC.
+02/13/96 - Roger Deschner - Original version.
+*/
+'CP TERMINAL LINEND #'
+'CP SET PF06 RETRIEVE'
+'CP SET PF18 RETRIEVE'
+'CP LINK * 3E1 3E1 RR' /* Change this address if you wish */
+'ACCESS 3E1 C' /* Access this server's tailorable minidisk */
+
+'GLOBALV SELECT SPBACKUP SETL QBACKUP 0'
+
+'PIPE CP QUERY USER' USERID() '| VAR qustring'
+PARSE UPPER VAR qustring . . termaddr .
+IF (termaddr ^= 'DSC') THEN DO
+ /* Tell the human user how to do our thingy */
+ SAY 'Terminal session detected. Entering CMS.'
+ SAY 'Enter the command "PROCESS" to continue as though disconnected.'
+END
+ELSE DO
+ /* Do our thingy */
+ PUSH 'EXEC PROCESS'
+END
+EXIT
diff --git a/vmworkshop-vmarcs/1996/spbackup/sat.tapepool b/vmworkshop-vmarcs/1996/spbackup/sat.tapepool
new file mode 100644
index 0000000..d554db1
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/sat.tapepool
@@ -0,0 +1,21 @@
+* THIS IS A SPBACKUP TAPEPOOL FOR SATURDAY MORNING JOBS
+RDSA00
+RDSA01
+RDSA02
+RDSA03
+RDSA04
+RDSA05
+RDSA06
+RDSA07
+RDSA08
+RDSA09
+RDSA10
+RDSA11
+RDSA12
+RDSA13
+RDSA14
+RDSA15
+RDSA16
+RDSA17
+RDSA18
+RDSA19
diff --git a/vmworkshop-vmarcs/1996/spbackup/spbackup.config b/vmworkshop-vmarcs/1996/spbackup/spbackup.config
new file mode 100644
index 0000000..e41e0f0
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/spbackup.config
@@ -0,0 +1,10 @@
+* SPBACKUP CONFIG
+*
+* (Comments are indicated by a * in column 1, like this line.)
+*
+* List of authorized users for SPBACKUP
+*
+* The systems guy:
+MAINT
+* The operator:
+OPERATOR
\ No newline at end of file
diff --git a/vmworkshop-vmarcs/1996/spbackup/spbackup.exec b/vmworkshop-vmarcs/1996/spbackup/spbackup.exec
new file mode 100644
index 0000000..d27db2e
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/spbackup.exec
@@ -0,0 +1,289 @@
+/* SPBACKUP EXEC:
+Driver routine to prompt you through all SPBACKUP operations.
+
+PREREQUISITES:
+GETFMADR, from the CMS Utilities/IPF set of tools.
+
+MODIFICATION HISTORY:
+04/01/96 - Roger Deschner - Original Version
+
+*/
+ADDRESS COMMAND
+
+tapepools = 'SUN MON TUE WED THU FRI SAT IPL'
+
+PARSE UPPER ARG ')' specialkey specialopts
+finalrc = 0
+SELECT
+ WHEN (specialkey = '') THEN DO
+ 'VMFCLEAR'
+ DO FOREVER
+ SAY ' SPBACKUP Spool Backup System'
+ SAY ' '
+ SAY 'Type a letter and press ENTER:'
+ SAY ' '
+ SAY 'Q QUERY current status of backup jobs'
+ SAY 'S START a backup job'
+ SAY 'D Change number of DRIVES currently in use for backup.'
+ SAY 'C CANCEL currently running backup job.'
+ SAY 'R RESTORE one or more files for a user'
+ SAY ' '
+ SAY '...or just press enter without typing anything to leave.'
+ SAY ' '
+ PARSE UPPER PULL answer .
+ SELECT
+ WHEN (answer = '') THEN EXIT 0
+ WHEN (answer = 'Q') THEN 'CP SMSG SPBACKUP BACKUP QUERY'
+ WHEN (answer = 'S') THEN CALL Starter
+ WHEN (answer = 'D') THEN CALL Driver
+ WHEN (answer = 'C') THEN CALL Canceller
+ WHEN (answer = 'R') THEN DO
+ SAY 'NOTE SYSTEMS, fully describing what you want restored.'
+ SAY 'Include: userid, last date backed up, spool file number',
+ 'or any other'
+ SAY 'identifying information. NOTICE: Spool backups are only',
+ 'kept for 7 days,'
+ SAY 'so if your file was backed up nearly that long ago,',
+ 'notify SYSTEMS _now_'
+ SAY 'in person or by paging, so that the backup',
+ 'tapes will not be'
+ SAY 'written over before your files can be restored.'
+ END
+ OTHERWISE DO
+ 'VMFCLEAR'
+ SAY ' '
+ SAY '**ERROR** Invalid choice. Try again.'
+ SAY ' '
+ ITERATE
+ END
+ END
+ LEAVE
+ END
+ END
+ OTHERWISE DO
+ SAY 'iNVALID KEYWORD.'
+ finalrc = 16
+ END
+END
+
+
+RETURN finalrc
+
+
+Qbackup:
+/**********************************************************************\
+* FUNCTION SUBPROGRAM to determine if a backup is already running. *
+\**********************************************************************/
+/* To call: xyzzy = QBACKUP()
+
+ or: CALL QBACKUP
+ xyzzy = result
+
+ Returns as its value:
+ 0 = backup is not running. No message issued.
+ 1 = backup is running. No message issued.
+ 55 = Server not responding. Message already issued.
+*/
+
+'CP SET SMSG IUCV'
+'PIPE (ENDCHAR ?)',
+ ' STARMSG *MSG CP SMSG SPBACKUP DATA QBACKUP',
+ '| SPECS 17-* 1', /* strip starmsg and RSCS prefix */
+ '| STEM response.',
+ '| FIND END OF RESPONSE', /* end of response */
+ '| Stop: FANINANY',
+ '| SPECS /PIPMOD STOP/ 1', /* Stop pipeline */
+ '| COMMAND',
+ '?',
+ ' LITERAL +15', /* specify delay */
+ '| DELAY', /* timeout if no response*/
+ '| Stop:'
+src = rc
+'CP SET SMSG OFF'
+PARSE UPPER VAR response.1 vname '=' value .
+IF (value = '') THEN DO
+ SAY 'Server not responding.'
+ result = 55
+ RETURN
+END
+ELSE RETURN value
+
+
+Starter:
+/**********************************************************************\
+* Start a backup job *
+\**********************************************************************/
+
+CALL QBACKUP /* Ask server if a backup is running */
+SELECT
+ WHEN (result = 0) THEN NOP /* Goodness! */
+ WHEN (result = 1) THEN DO
+ SAY 'Cannot start a backup because one is already running.'
+ SAY 'Use the Query option for more details.'
+ finalrc = 40
+ RETURN
+ END
+ OTHERWISE DO /* Any other RESULT is an error code */
+ finalrc = result
+ RETURN
+ END
+END
+
+/* Set default tapepool to today: */
+dow = TRANSLATE(SUBSTR(DATE('W'),1,3))
+
+SAY ' '
+SAY 'Enter tapepool name:' tapepools','
+SAY 'or press Enter for default' dow '-'
+PARSE UPPER PULL tapepool .
+IF (tapepool = '') THEN tapepool = dow
+IF (WORDPOS(tapepool,tapepools) < 1) THEN DO
+ SAY 'Tape pool must be one of' tapepools
+ finalrc = 16
+ RETURN
+END
+SAY 'Using tapepool' tapepool'.'
+
+SAY ' '
+SAY 'Enter number drives, from 2 to 12, or just press Enter to quit.'
+SAY '(3 to 5 drives usually provides best performance.)'
+PARSE PULL ndrives .
+
+IF (DATATYPE(ndrives,'W') <> 1) THEN DO
+ SAY 'Number of drives must be a whole number.'
+ finalrc = 16
+ RETURN
+END
+IF ((ndrives < 2) | (ndrives > 12)) THEN DO
+ SAY 'Number of drives must be between 2 and 12.'
+ finalrc = 16
+ RETURN
+END
+
+'VMFCLEAR'
+SAY ' '
+SAY 'You have requested to start a spool backup using Tape Pool' tapepool
+SAY 'on' ndrives 'tape drives. This will overwrite and erase the',
+ 'previous'
+SAY 'backup which was written on Tape Pool' tapepool'. This backup',
+ 'will take'
+SAY 'from 1.5 to 3 hours to run. Type "Y" to confirm and start this',
+ 'backup,'
+SAY 'or type anything else to quit now without starting anything.'
+SAY ' '
+SAY 'Start the backup now? (Y or N) -'
+PARSE UPPER PULL answer .
+IF (answer = 'Y') THEN 'CP SMSG SPBACKUP BACKUP START' tapepool ndrives
+finalrc = 0
+RETURN
+
+
+Driver:
+/**********************************************************************\
+* Change number of drives in use *
+\**********************************************************************/
+
+CALL QBACKUP /* Ask server if a backup is running */
+SELECT
+ WHEN (result = 1) THEN NOP /* Goodness! */
+ WHEN (result = 0) THEN DO
+ SAY 'Cannot change number of drives because no backup is running.'
+ finalrc = 40
+ RETURN
+ END
+ OTHERWISE DO /* Any other RESULT is an error code */
+ finalrc = result
+ RETURN
+ END
+END
+
+/* How many drives is it running with at the present? */
+'CP SET SMSG IUCV'
+'PIPE (ENDCHAR ?)',
+ ' STARMSG *MSG CP SMSG SPBACKUP DATA NDRIVES',
+ '| SPECS 17-* 1', /* strip starmsg and RSCS prefix */
+ '| STEM response.',
+ '| FIND END OF RESPONSE', /* end of response */
+ '| Stop: FANINANY',
+ '| SPECS /PIPMOD STOP/ 1', /* Stop pipeline */
+ '| COMMAND',
+ '?',
+ ' LITERAL +15', /* specify delay */
+ '| DELAY', /* timeout if no response*/
+ '| Stop:'
+src = rc
+'CP SET SMSG OFF'
+PARSE UPPER VAR response.1 vname '=' value .
+IF (value = '') THEN DO
+ SAY 'Server not responding.'
+ finalrc = 55
+ RETURN
+END
+
+SAY ' '
+SAY 'Backup is presently running with' value 'drives.'
+SAY ' '
+SAY 'Enter new number of drives, or just press enter to do nothing -'
+PARSE PULL ndrives .
+IF (ndrives = '') THEN DO
+ finalrc = 0
+ RETURN
+END
+
+IF (DATATYPE(ndrives,'W') <> 1) THEN DO
+ SAY 'Number of drives must be a whole number.'
+ finalrc = 16
+ RETURN
+END
+IF ((ndrives < 2) | (ndrives > 12)) THEN DO
+ SAY 'Number of drives must be between 2 and 12.'
+ finalrc = 16
+ RETURN
+END
+
+'CP SMSG SPBACKUP BACKUP DRIVES' ndrives
+
+finalrc = 0
+RETURN
+
+
+Canceller:
+/**********************************************************************\
+* Cancel a running BACKUP job *
+\**********************************************************************/
+
+CALL QBACKUP /* Ask server if a backup is running */
+SELECT
+ WHEN (result = 1) THEN NOP /* Goodness! */
+ WHEN (result = 0) THEN DO
+ SAY 'Cannot cancel backup, because no backup is running.'
+ finalrc = 40
+ RETURN
+ END
+ OTHERWISE DO /* Any other RESULT is an error code */
+ finalrc = result
+ RETURN
+ END
+END
+
+'VMFCLEAR'
+SAY ' '
+SAY ' '
+SAY ' '
+SAY ' '
+SAY ' '
+SAY ' '
+SAY 'Are you really sure you want to cancel the Spool Backup Job?'
+SAY ' '
+SAY 'Type "CANCEL" again now to verify, or anything else to not cancel:'
+PARSE UPPER PULL answer .
+IF (answer <> 'CANCEL') THEN DO
+ SAY 'Not cancelled.'
+ finalrc = 8
+ RETURN
+END
+
+'CP SMSG SPBACKUP BACKUP CANCEL'
+
+finalrc = 0
+RETURN
diff --git a/vmworkshop-vmarcs/1996/spbackup/spbackup.names b/vmworkshop-vmarcs/1996/spbackup/spbackup.names
new file mode 100644
index 0000000..7898b6d
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/spbackup.names
@@ -0,0 +1 @@
+:nick.ABENDMSG :list.MAINT
diff --git a/vmworkshop-vmarcs/1996/spbackup/sun.tapepool b/vmworkshop-vmarcs/1996/spbackup/sun.tapepool
new file mode 100644
index 0000000..dc5a9ed
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/sun.tapepool
@@ -0,0 +1,21 @@
+* THIS IS A SPBACKUP TAPEPOOL FOR SUNDAY MORNING JOBS
+RDSU00
+RDSU01
+RDSU02
+RDSU03
+RDSU04
+RDSU05
+RDSU06
+RDSU07
+RDSU08
+RDSU09
+RDSU10
+RDSU11
+RDSU12
+RDSU13
+RDSU14
+RDSU15
+RDSU16
+RDSU17
+RDSU18
+RDSU19
diff --git a/vmworkshop-vmarcs/1996/spbackup/test.tapepool b/vmworkshop-vmarcs/1996/spbackup/test.tapepool
new file mode 100644
index 0000000..74a060e
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/test.tapepool
@@ -0,0 +1,7 @@
+* This is a SPBACKUP tapepool for development and testing.
+MVS1
+MVS2
+MVS3
+MVS4
+MVS5
+MVS6
diff --git a/vmworkshop-vmarcs/1996/spbackup/thu.tapepool b/vmworkshop-vmarcs/1996/spbackup/thu.tapepool
new file mode 100644
index 0000000..f70b8b5
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/thu.tapepool
@@ -0,0 +1,21 @@
+* This is a SPBACKUP tapepool for Thursday jobs
+RDTH00
+RDTH01
+RDTH02
+RDTH03
+RDTH04
+RDTH05
+RDTH06
+RDTH07
+RDTH08
+RDTH09
+RDTH10
+RDTH11
+RDTH12
+RDTH13
+RDTH14
+RDTH15
+RDTH16
+RDTH17
+RDTH18
+RDTH19
diff --git a/vmworkshop-vmarcs/1996/spbackup/tue.tapepool b/vmworkshop-vmarcs/1996/spbackup/tue.tapepool
new file mode 100644
index 0000000..ccf65ae
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/tue.tapepool
@@ -0,0 +1,21 @@
+* THIS IS A SPBACKUP TAPEPOOL FOR TUESDAY MORNING JOBS
+RDTU00
+RDTU01
+RDTU02
+RDTU03
+RDTU04
+RDTU05
+RDTU06
+RDTU07
+RDTU08
+RDTU09
+RDTU10
+RDTU11
+RDTU12
+RDTU13
+RDTU14
+RDTU15
+RDTU16
+RDTU17
+RDTU18
+RDTU19
diff --git a/vmworkshop-vmarcs/1996/spbackup/wed.tapepool b/vmworkshop-vmarcs/1996/spbackup/wed.tapepool
new file mode 100644
index 0000000..1806560
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/spbackup/wed.tapepool
@@ -0,0 +1,21 @@
+* THIS IS A SPBACKUP TAPEPOOL FOR WEDNESDAY MORNING JOBS
+RDWE00
+RDWE01
+RDWE02
+RDWE03
+RDWE04
+RDWE05
+RDWE06
+RDWE07
+RDWE08
+RDWE09
+RDWE10
+RDWE11
+RDWE12
+RDWE13
+RDWE14
+RDWE15
+RDWE16
+RDWE17
+RDWE18
+RDWE19
diff --git a/vmworkshop-vmarcs/1996/sysoptap/README.md b/vmworkshop-vmarcs/1996/sysoptap/README.md
new file mode 100644
index 0000000..3f4cb27
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/sysoptap/README.md
@@ -0,0 +1,22 @@
+# SYSOPTAP
+A Disconnected Service Virtual for Displaying VMTAPE mounts
+
+Version 1.2, May, 1996
+Roger Deschner (rogerd@uic.edu)
+University of Illinois at Chicago
+
+SYSOPTAP runs disconnected, and drives any number of ATTACHED or DIALED displays, showing in large letters the tapes to be mounted for VMTAPE. These displays are constantly updated, and so can be located in various areas to tell operators and tape librarians what is being requested.
+
+SYSOPTAP also intercepts FOREIGN tape mounts and assumes that they are for Standard Labeled tapes which are being mounted by their internal label name, such as from the DMSTVI exit. Then it looks these up in the TMC and shows the slot number for the operator to find that tape. Such tapes must belong to the user requesting the mount, due to the possibility that there may be more than one tape in the system with the same internal label name.
+
+## REQUIREMENTS
+
+Sterling VM:Tape (of course)
+WAKEUP from CMS Utilities (can be replaced by HMF or PIPE)
+CMS Pipelines 1.0106 (CMS8) or later
+
+See file SYSOPTAP INSTALL for installation details.
+
+Even if you have no interest in tape mounting, SYSOPTAP is a working example of a server which runs disconnected, and which drives displays connected to it using DEDICATE, ATTACH, or DIAL commands. These are driven using the CMS Pipelines FULLSCREEN device driver. This is a superior method, avoiding most of the problems of attempting to use a virtual machine's console as an informational display.
+
+Roger Deschner University of Illinois at Chicago R.Deschner@uic.edu
diff --git a/vmworkshop-vmarcs/1996/sysoptap/biglet5.rexx b/vmworkshop-vmarcs/1996/sysoptap/biglet5.rexx
new file mode 100644
index 0000000..337fb60
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/sysoptap/biglet5.rexx
@@ -0,0 +1,164 @@
+/* BIGLET REXX: This CMS Pipelines filter converts each line of input
+into five output lines, containing the input translated to big 5x5
+characters. Beware that output lines will be 6 times as long as the
+input, and that there will be 5 times as many of them.
+
+MODIFICATION HISTORY: */
+/* BIGM EXEC started Saturday, 17 Mar 1984 14:16:26 by MW9 */
+/* Exec to sent a big message - R. Kandhal */
+/* REVISED BY MSP 4/20/83 (quicker!) */
+/* Revised by K27 4/25/83 (Allow sEnd to a remote site) */
+/* Revised by MW9 3/17/84 (Translated into REXX) */
+/* (Takes nicknames!) */
+/* 3/20/84 (No need for 'BLOCKPS') */
+/* 3/21/84 (Lower case letters!) */
+/* getname proc courtesy of BSD */
+/* 02/12/91 - Roger Deschner, UIC - convert to Pipelines filter. */
+/* 04/02/93 - Roger Deschner, UIC - 5x5 letter version */
+
+charstring = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
+charstring = charstring||'~|@#$%^&*()-_=+!\:;''"{},./?<>'
+charstring = charstring||'abcdefghijklmnopqrstuvwxyz'
+bs.1 = ' A BBBB CCCC DDDD EEEEE '
+bs.2 = ' A A B B C D D E '
+bs.3 = 'A A BBBB C D D EEE '
+bs.4 = 'AAAAA B B C D D E '
+bs.5 = 'A A BBBB CCCC DDDD EEEEE '
+
+bs.1 = bs.1||'FFFFF GGGG H H III J '
+bs.2 = bs.2||'F G H H I J '
+bs.3 = bs.3||'FFF G GG HHHHH I J '
+bs.4 = bs.4||'F G G H H I J J '
+bs.5 = bs.5||'F GGGG H H III JJJ '
+
+bs.1 = bs.1||'K K L M M N N OOOOO '
+bs.2 = bs.2||'K K L MM MM NN N O O '
+bs.3 = bs.3||'KKK L M M M N N N O O '
+bs.4 = bs.4||'K K L M M N NN O O '
+bs.5 = bs.5||'K K LLLLL M M N N OOOOO '
+
+bs.1 = bs.1||'PPPP QQQ RRRR SSSSS TTTTT '
+bs.2 = bs.2||'P P Q Q R R S T '
+bs.3 = bs.3||'PPPP Q Q Q RRRR SSSSS T '
+bs.4 = bs.4||'P Q QQ R R S T '
+bs.5 = bs.5||'P QQQQ R R SSSSS T '
+
+bs.1 = bs.1||'U U V V W W X X Y Y '
+bs.2 = bs.2||'U U V V W W X X Y Y '
+bs.3 = bs.3||'U U V V W W W X Y '
+bs.4 = bs.4||'U U V V WW WW X X Y '
+bs.5 = bs.5||' UUU V W W X X Y '
+
+bs.1 = bs.1||'ZZZZZ 000 1 222 33333 '
+bs.2 = bs.2||' Z 0 0 11 2 2 3 '
+bs.3 = bs.3||' Z 0 0 1 22 33 '
+bs.4 = bs.4||' Z 0 0 1 2 3 '
+bs.5 = bs.5||'ZZZZZ 000 111 22222 3333 '
+bs.1 = bs.1||' 4 55555 666 77777 888 '
+bs.2 = bs.2||' 44 5 6 7 8 8 '
+bs.3 = bs.3||' 4 4 5555 6666 7 888 '
+bs.4 = bs.4||'44444 5 6 6 7 8 8 '
+bs.5 = bs.5||' 4 5555 666 7 888 '
+
+bs.1 = bs.1||' 999 ~ ~ | @@@@@ # # '
+bs.2 = bs.2||'9 9 ~ ~~ | @ @ @ ##### '
+bs.3 = bs.3||' 9999 | @ @@@ # # '
+bs.4 = bs.4||' 9 | @ ##### '
+bs.5 = bs.5||' 999 | @@@@ # # '
+
+bs.1 = bs.1||'$$$$$ % % ^ &&& * * '
+bs.2 = bs.2||'$ $ % ^ ^ & & * * '
+bs.3 = bs.3||'$$$$$ % &&&& ***** '
+bs.4 = bs.4||' $ $ % & & * * '
+bs.5 = bs.5||'$$$$$ % % &&&&& * * '
+
+bs.1 = bs.1||' ( ) '
+bs.2 = bs.2||' ( ) ===== '
+bs.3 = bs.3||' ( ) ----- '
+bs.4 = bs.4||' ( ) ===== '
+bs.5 = bs.5||' ( ) '
+
+bs.1 = bs.1||' ! \ '
+bs.2 = bs.2||' + ! \ '
+bs.3 = bs.3||'+++++ ! \ '
+bs.4 = bs.4||' + \ '
+bs.5 = bs.5||' ! \ '
+
+/* The following is NOT an error - double quotes to create single */
+bs.1 = bs.1||' ::: ;;; '''' " " {{{ '
+bs.2 = bs.2||' ::: ;;; '' { '
+bs.3 = bs.3||' { '
+bs.4 = bs.4||' ::: ;;; { '
+bs.5 = bs.5||' ::: ; {{{ '
+
+bs.1 = bs.1||' }}} / ????? '
+bs.2 = bs.2||' } / ? '
+bs.3 = bs.3||' } / ?? '
+bs.4 = bs.4||' } ,, .. / '
+bs.5 = bs.5||' }}} , .. / ? '
+
+bs.1 = bs.1||' < > aaa b '
+bs.2 = bs.2||' < > a b ccc '
+bs.3 = bs.3||'< > aaaa bbbb c '
+bs.4 = bs.4||' < > a a b b c '
+bs.5 = bs.5||' < > aaa dbbb ccc '
+
+bs.1 = bs.1||' d ff ggg h '
+bs.2 = bs.2||' d eee f g g h '
+bs.3 = bs.3||' dddd eeeee fff gggg hhhh '
+bs.4 = bs.4||'d d e f g h h '
+bs.5 = bs.5||' dddd eee f gg h h '
+
+bs.1 = bs.1||' i j k ll '
+bs.2 = bs.2||' k l mmmm '
+bs.3 = bs.3||' ii j k k l m m m '
+bs.4 = bs.4||' i j j kkk l m m m '
+bs.5 = bs.5||' iii jjj k kk lll m m m '
+
+bs.1 = bs.1||' '
+bs.2 = bs.2||'nnnn ooo pppp qqqq rrrr '
+bs.3 = bs.3||'n n o o p p q q r '
+bs.4 = bs.4||'n n o o pppp qqqq r '
+bs.5 = bs.5||'n n ooo p q r '
+
+bs.1 = bs.1||' '
+bs.2 = bs.2||' sss t u u v v w w '
+bs.3 = bs.3||' s ttt u u v v w w w '
+bs.4 = bs.4||' s t u u v v w w w '
+bs.5 = bs.5||' sss tt uuuu v w w '
+
+bs.1 = bs.1||' '
+bs.2 = bs.2||'x x y y zzzzz '
+bs.3 = bs.3||' x.x y y zz '
+bs.4 = bs.4||' x^x y z '
+bs.5 = bs.5||'x x y zzzzz '
+
+SIGNAL ON ERROR
+DO FOREVER /* Do until EOF */
+ 'READTO record' /* Suck from pipe */
+ num = LENGTH(record)
+ ostr. = ''
+ pos = 0
+ DO i = 1 TO num
+ letter = SUBSTR(record,i,1)
+ letnum = index(charstring,letter)
+ IF (letnum = 0) THEN DO /* Not found? Insert blank. */
+ pos = pos + 6
+ END
+ ELSE DO /* Move in letter. */
+ ipos = (((letnum - 1) * 6 ) + 1)
+ DO j = 1 TO 5
+ ostr.j = LEFT(ostr.j,pos) || SUBSTR(bs.j,ipos,6)
+ END
+ pos = pos + 6
+ END
+ END
+
+ /* Output the string */
+
+ DO j = 1 TO 5
+ 'OUTPUT' ostr.j /* Blow into pipe */
+ END
+END
+
+ERROR: EXIT rc*(rc<>12) /* On normal eof, set rc=0 */
diff --git a/vmworkshop-vmarcs/1996/sysoptap/process.exec b/vmworkshop-vmarcs/1996/sysoptap/process.exec
new file mode 100644
index 0000000..d79157a
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/sysoptap/process.exec
@@ -0,0 +1,31 @@
+/* This program waits for any MSG interupt and refreshes its displays.
+
+MODIFICATION HISTORY:
+04/06/93 - Roger - Original Version. From example in WAKEUP help file.
+*/
+/* Invoke WAKEUP first so it will be ready to receive msgs */
+/* This call also issues a 'SET MSG IUCV' command. */
+
+"WAKEUP +0 (IUCVMSG"
+call domsg uid,text /* paint first display */
+
+/* In this loop, we wait for a message to arrive and */
+/* process it when it does. If the "operator" types on */
+/* the console (rc=6) then leave the exec. */
+
+Do forever;
+ 'wakeup (iucvmsg' /* wait for a message */
+ if rc /=5 then leave; /* Leave when its not a msg */
+ parse pull type uid text /* get the msg details... */
+ call domsg uid,text /* go process the command */
+end;
+
+ /* when its time to quit, come here */
+ xit:
+
+ 'WAKEUP RESET'; /* turn messages from IUCV to ON */
+ exit;
+
+DOMSG:
+'EXEC QMOUNT'
+RETURN
diff --git a/vmworkshop-vmarcs/1996/sysoptap/profile.exec b/vmworkshop-vmarcs/1996/sysoptap/profile.exec
new file mode 100644
index 0000000..aeb7aac
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/sysoptap/profile.exec
@@ -0,0 +1,10 @@
+/* This profile is for SYSOPTAP. It starts up the tape monitoring
+process
+*/
+'CP SET EMSG ON'
+'CP SET PF06 RETRIEVE'
+'CP SET PF18 RETRIEVE'
+/* These are for future use by people issuing the DIAL command: */
+'CP DEFINE GRAF 4A1 3270'
+'CP DEFINE GRAF 4A2 3270'
+'EXEC PROCESS'
diff --git a/vmworkshop-vmarcs/1996/sysoptap/qmount.exec b/vmworkshop-vmarcs/1996/sysoptap/qmount.exec
new file mode 100644
index 0000000..a38813c
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/sysoptap/qmount.exec
@@ -0,0 +1,393 @@
+/* This program queries VMTAPE and displays any pending mounts in big
+LETTERS ON THE SCREEN of all attached GRAF devices which are listed in
+file QMOUNT CONFIG. Any devices listed in QMOUNT CONFIG which are not
+actually attached and are GRAF devices, are ignored without causing an
+error. Note that if none of the devices listed in QMOUNT CONFIG are
+attached at the present time, this program is effectively a NOP.
+
+If (TEST is specified, output is written to this screen, which is
+assumed to be 24x80.
+
+Devices may be attached by any of the following three methods:
+o DEDICATE vdev rdev <-- in CP Directory for this uid
+o CP ATTACH rdev uid vdev <-- from any priveleged ID
+o CP DEFINE GRAF vdev 3270 <-- on this uid, and then...
+ DIAL uid vdev <-- ...from any terminal
+
+VMTAPE RESPONSES: (characters 4-6 may be any module name!)
+
+VMTQRY061I There are no mounts pending.
+VMTMNT057A Mount LIBRARY volume '000058' on 1888, NO RING, for U52983 0181.
+VMTMNT057A Mount FOREIGN volume 'IL336' on 0880, NO RING, for VMCENTER 0181.
+VMTMNT057A Mount LIBRARY volume '000058' on 1889, RING IN, for U52983 0181.
+VMTMNT057A Mount LIBRARY volume '000029' on 1882, RING IN, for U09046 0181.
+VMTMNT057A Mount LIBRARY volume 'VMB510' on 1883, RING IN, for VMBACKUP 0320.
+VMTMNT280A Mount selected COMPCTR SCRATCH volume 000342 on '188B', RING IN, for U52983 0181.
+VMTDRV144I Waiting for a 18track XF BPI drive for VMBACKUP 0420 VXB510.
+VMTVOL699I MOUNT BATCHA02 0181 waiting for volume N09018.
+VMTDRV144I Waiting for a 9-track 1600 BPI drive for BATCHA01 0181 N09018.
+VMTVOL699I MOUNT BATCHA02 0181 waiting for volume N09018.
+VMTDRV144I Waiting for a 9-track 1600 BPI drive for BATCHA01 0181 N09018.
+VMTMNT057A Mount LIBRARY volume 'VMA270' on 1882, NO RING, for VMBACKUP 0310.
+VMTMNT057A Mount LIBRARY volume 'VMA326' on 1888, NO RING, for VMBACKUP 0510.
+VMTMNT057A Mount LIBRARY volume 'VMA333' on 1889, NO RING, for VMBACKUP 0710.
+VMTVOL699I MOUNT BATCHA02 0181 waiting for volume N09018.
+VMTVOL701I MOUNT VMBACKUP 0610 waiting for volume VMA326 which is mounted on 1888.
+VMTDRV144I Waiting for a 9-track 1600 BPI drive for BATCHA01 0181 N09018.
+VMTMNT051T Mount IL336 0181 canceled by U52983.
+
+MODIFICATION HISTORY:
+05/26/96 - Roger - Randomize location of "No Tape Mounts Waiting" message
+ to prevent screen burn-in
+04/08/96 - Roger - Fix Rexx interrupt trying to issue err msg about
+ can't mount foriegn tape.
+06/06/95 - Roger - Don't test module name in chars 4-6 of msgs
+12/07/94 - Roger - fix tiny bug in mounting scratch tapes.
+06/03/94 - Roger Deschner - Deal with SELECTED SCRATCH requests; look up
+ slots for FOREIGN tapes.
+07/06/93 - Roger Deschner - Check for "NOT DIALED" response
+04/13/93 - Roger Deschner - Deal with pending alloc messages
+04/02/93 - Roger Deschner - Original version
+*/
+ADDRESS COMMAND
+operid = 'OPERATOR' /* Userid of system console operator */
+vmtapeid = 'VMTAPE' /* Userid of VM:Tape service virtual. */
+PARSE UPPER ARG '(' testparm .
+
+IF (testparm = 'TEST') THEN testparm = 1
+ ELSE testparm = 0
+IF (testparm) THEN configft = 'CONFTEST'
+ ELSE configft = 'CONFIG'
+
+/* Find CONFIG file */
+configfn = USERID()
+'PIPE LITERAL' configfn configft,
+ '| STATE NOFORMAT',
+ '| COUNT LINES',
+ '| VAR cfexists'
+IF (cfexists < 1) THEN DO
+ configfn = 'QMOUNT'
+ 'PIPE LITERAL' configfn configft,
+ '| STATE NOFORMAT',
+ '| COUNT LINES',
+ '| VAR cfexists'
+ IF (cfexists < 1) THEN DO
+ SAY 'No CONFIG file found. Unable to continue'
+ EXIT 99
+ END
+END
+
+/* Read CONFIG file */
+'PIPE (ENDCHAR ?) <' configfn configft '*',
+ '| NFIND *' ||, /* Eliminate comments */
+ '| FOREIGN: NFIND FOREIGN' ||,
+ '| STEM displays.', /* List of displays configured */
+'? FOREIGN:',
+ '| SPECS WORDS 2 1',
+ '| JOIN * / /', /* String 'em all together */
+ '| VAR foreignids' /* List of users allowed to mount FOREIGN */
+
+j = 0
+DO i = 1 TO displays.0
+ PARSE UPPER VAR displays.i cfaddr
+ /* What's at that virtual address? */
+ 'PIPE CP QUERY VIRTUAL' SUBWORD(displays.i,1,1) '| VAR cpresp'
+ orc = rc
+ /* Is it what we want? A valid defined GRAF? */
+ PARSE VAR cpresp grafornot . dialed
+ IF ((orc = 0),
+ & ((grafornot = 'GRAF') | ((grafornot = 'CONS') & (testparm = 1))),
+ & (dialed <> 'NOT DIALED'),
+ ) THEN DO
+ j = j + 1
+ PARSE UPPER VAR displays.i disaddr.j dislines.j discols.j .
+ END
+END
+disaddr.0 = j
+dislines.0 = j
+discols.0 = j
+
+IF (testparm) THEN DO /* This is a test */
+ 'PIPE < QMOUNT TEST * | NFIND * | STEM mountr.'
+END
+ELSE DO /* This is not a test */
+ 'PIPE CMS' vmtapeid 'QUERY | STEM mountr.'
+END
+
+k = 0
+DO i = 1 TO mountr.0
+ PARSE VAR mountr.i msgid 1 msg1 4 . 7 msg2 .
+ SELECT
+ /* Eliminate messages we are not interested in */
+ WHEN ((msg1 = 'VMT') & (msg2 = '061I')) THEN NOP
+ WHEN ((msg1 = 'VMT') & (msg2 = '145I')) THEN NOP
+ WHEN ((msg1 = 'VMT') & (msg2 = '057A')) THEN DO
+ /* Regular mount request */
+ /* Variable is the userid who issued the MOUNT. But,
+ what if it is a BATCH job? In that case, we look up the ACIGROUP
+ and assign that to . Variable is always the
+ userid who instigated the request - whether by issuing a MOUNT on
+ his terminal, or by running a BATCH job containing a mount. */
+ PARSE VAR mountr.i . . qforeign . volser . drive rwstat 'for' ,
+ mountid vaddr .
+ volser = STRIP(volser,"B","'") /* Remove quotes */
+ drive = STRIP(drive,'B',',') /* Remove comma */
+ vaddr = STRIP(vaddr,'B','.') /* Remove period. */
+ IF (rwstat = 'RING IN,') THEN rwstat = 'W'
+ ELSE rwstat = ''
+ IF (SUBSTR(mountid,1,5) = 'BATCH') THEN DO
+ /* Get userid who ran the batch job */
+ 'PIPE CP QUERY ACIGROUP' mountid '| STEM answer.'
+ PARSE VAR answer.1 '=' originid .
+ IF (originid = '') THEN originid = mountid
+ END
+ ELSE DO
+ originid = mountid
+ END
+ IF (qforeign = 'FOREIGN') THEN DO
+ /* This defines VAR slotnum and STEM answer. */
+ CALL GETSLOT volser originid
+ extlabrc = result
+ IF (extlabrc <> 0) THEN DO /* ERROR RECOVERY */
+ /* No such tape, or duplicate tape */
+ IF (FIND(foreignids,originid) > 0) THEN DO
+ /* Allowed to mount FOREIGN */
+ k = k + 1
+ screenline.k = LEFT(volser,6) LEFT(drive,4) rwstat
+ k = k + 1
+ screenline.k = ' FOREIGN TAPE'
+ END
+ ELSE DO /* Not allowed to mount FOREIGN. Issue message. */
+ IF (testparm) THEN DO
+ 'PIPE STEM answer.',
+ '| LITERAL OPERATOR: The following message was sent',
+ 'to user' mountid'. They may call:',
+ '| SPECS /MSGNOH' USERID() '/ 1 1-* NEXT',
+ '| CP'
+ END /* of IF (testparm) THEN DO */
+ ELSE DO /* fer real */
+ 'PIPE STEM answer.',
+ '| SPECS /MSGNOH' mountid '/ 1 1-* NEXT',
+ '| CP'
+ 'PIPE STEM answer.',
+ '| LITERAL OPERATOR: The following message was sent',
+ 'to user' originid'. They may call:',
+ '| SPECS /MSGNOH' operid '/ 1 1-* NEXT',
+ '| CP'
+ IF (mountid <> originid) THEN DO /* Tell HIM too */
+ 'PIPE STEM answer.',
+ '| SPECS /MSGNOH' originid '/ 1 1-* NEXT',
+ '| CP'
+ END
+ ADDRESS CMS vmtapeid 'CANCEL' vaddr mountid
+ END /* of ELSE DO /* fer real */ */
+ END /* of ELSE DO/* Not allowed to mount FOREIGN. Issue message. */ */
+ END /* of IF (extlabrc <> 0) THEN DO /* ERROR RECOVERY */ */
+ ELSE DO /* Good lookup by Internal Label. Add lines to screen */
+ k = k + 1
+ screenline.k = LEFT(volser,6) LEFT(drive,4) rwstat
+ k = k + 1
+ screenline.k = ' Slot' slotnum
+ END
+ END
+ ELSE DO /* Not a foreign tape - add line to screen */
+ k = k + 1
+ screenline.k = LEFT(volser,6) LEFT(drive,4) rwstat
+ END
+ END
+ WHEN ((msg1 = 'VMT') & (msg2 = '280A')) THEN DO
+ /* Scratch mount request */
+ PARSE VAR mountr.i . . . . . . volser . drive rwstat 'for' .
+ drive = STRIP(drive,'B',',') /* Remove comma */
+ drive = STRIP(drive,"B","'") /* Remove quotes */
+ IF (rwstat = 'RING IN,') THEN rwstat = 'W'
+ ELSE rwstat = ''
+ k = k + 1
+ screenline.k = LEFT(volser,6) LEFT(drive,4) rwstat
+ END
+ /* Alloc pending */
+ WHEN ((msg1 = 'VMT') & (WORDPOS(msg2,'699I 701I 144I') > 0)) THEN DO
+ k = k + 1
+ screenline.k = 'Wait:' mountr.i
+ END
+ OTHERWISE DO /* Any other message */
+ k = k + 1
+ screenline.k = '*' mountr.i
+ END
+ END
+END
+screenline.0 = k
+/* debug trace */ 'PIPE STEM screenline. | CONSOLE'
+
+IF (screenline.0 = 0) THEN DO
+ randomize = 'YES'
+ screenline.1 = 'No Tape'
+ screenline.2 = 'Mounts'
+ screenline.3 = 'Waiting'
+ screenline.0 = 3
+END
+ELSE randomize = 'NO'
+
+DO j = 1 TO disaddr.0
+ /* Build the screen image in stem */
+
+ scnlines = TRUNC(dislines.j / 6)
+ screen.0 = 0 /* initialize stem for APPEND */
+ IF (screenline.0 <= scnlines) THEN DO
+ /* There's room to build big letters */
+ DO I = 1 TO screenline.0
+ PARSE VAR screenline.i w1 wrest
+ /* Is it a line we need to catenate small letters to? */
+ IF (WORDPOS(w1,'* Wait:') > 0) THEN DO
+ 'PIPE VAR w1 | BIGLET5 | STEM screen. APPEND'
+ lbigw1 = LENGTH(w1) * 6
+ stcol = lbigw1 + 1 /* Column to begin placing stuff */
+ lenstuff = (discols.j - lbigw1) - 1 /* 1 less for ctlchar */
+ k1 = screen.0 - 4
+ DO k = k1 TO screen.0
+ /* Find last blank, starting at LENSTUFF+1 working backwards */
+ m = lenstuff+1
+ DO WHILE (SUBSTR(wrest,m,1) ^= ' ')
+ m = m - 1
+ IF (m = 0) THEN m = lenstuff+1 /* Word too long? CHOP! */
+ END
+ screen.k = LEFT(screen.k,lbigw1) LEFT(wrest,(m-1))
+ wrest = SUBSTR(wrest,(m+1))
+ IF (wrest = '') THEN LEAVE
+ END
+ END
+ ELSE DO
+ 'PIPE VAR screenline.i | BIGLET5 | STEM screen. APPEND'
+ END
+ /* Put blank line, if not last. */
+ IF (i < screenline.0) THEN 'PIPE LITERAL | STEM screen. APPEND'
+ END
+ END
+ ELSE DO
+ /* Too many lines for this display. Use reduced format.*/
+ 'PIPE LITERAL TAPE MOUNTS:|',
+ 'BIGLET5|',
+ 'APPEND LITERAL |',
+ 'APPEND STEM screenline.|',
+ 'STEM screen.'
+ END
+ /* Are we going to randomize the position of "No Tape Mounts Waiting"
+ to prevent screen burn-in? */
+ IF (randomize = 'YES') THEN DO
+ 'PIPE LITERAL ', /* Make a blank record */
+ '| DUPLICATE' RANDOM(0,6), /* Make 0-6 copies of it */
+ '| APPEND STEM screen.', /* Add our stem */
+ '| DROP FIRST 1', /* Kill the original blank */
+ '| SPECS 1-*' RANDOM(1,38), /* Shift left */
+ '| BUFFER', /* ooo la la */
+ '| STEM screen.'
+ END
+ /* Write it out */
+ IF (testparm) THEN DO
+ 'VMFCLEAR'
+ 'PIPE STEM screen. | CONSOLE'
+ END
+ ELSE DO
+ CALL WRIT3270
+ END
+END
+RETURN
+
+WRIT3270: /* EXPOSE all vars */
+/**********************************************************************\
+Example 2: The following exec writes a data stream to a 3270 device at
+virtual address specified by the argument. A read is not performed.
+
+MODIFICATION HISTORY:
+04/03/93 - Roger - From IBM manual "CMS Pipelines reference"
+\**********************************************************************/
+EraseWrite = 'C0'x /* Erase/Write command */
+WriteControlChar = '03'x /* Write Control Character */
+SetBufferAddress = '11'x /* Set Buffer Address order */
+InsertCursor = '13'x /* Insert Cursor order */
+StartField = '1D'x /* Start Field order */
+AttrProtect = '28'x /* Unprotected attribute byte */
+/* DataStreamOut = EraseWrite||WriteControlChar||SetBufferAddress||, */
+/* '0000'x||StartField||AttrProtect||'You have connected to', */
+/* userid()||SetBufferAddress||'0000'x||InsertCursor */
+DataStreamOut = EraseWrite||WriteControlChar
+DO i = 1 TO screen.0
+ /* Calculate 3270 "14-bit" screen address */
+ scnpos = RIGHT(D2C((i-1)*discols.j),2,'00'x)
+ DataStreamOut = DataStreamOut ||,
+ SetBufferAddress || scnpos || StartField || AttrProtect || screen.i
+END
+DataStreamOut = DataStreamOut ||,
+ SetBufferAddress||'0000'x||InsertCursor
+ /* Build the data stream */
+'PIPE',
+ 'VAR DataStreamOut', /* Get outbound data stream */
+ '| FULLSCREEN' disaddr.j 'NOREAD', /* Write it to device 0500 */
+ '| HOLE' /* Discard the results */
+RETURN rc /* Exit */
+
+GETSLOT: PROCEDURE EXPOSE slotnum answer. vmtapeid
+/*********************************************************************\
+* *
+* SUBROUTINE GETSLOT *
+* *
+\*********************************************************************/
+/*
+List the external label of all tapes signed in for a user which have a
+internal label specified.
+*/
+PARSE UPPER ARG tapevol ownerid .
+slotnum = ''
+IF (ownerid = '*') THEN ownerid = USERID()
+IF (ownerid = '') THEN RETURN 24
+IF (tapevol = '') THEN RETURN 24
+
+/* Do this to avoid unnecessary error when asking about own tapes */
+IF (ownerid <> USERID()) THEN ownpart = 'OWNEDBY' ownerid
+ ELSE ownpart = ''
+
+'PIPE CMS' vmtapeid 'LIST (BIN' ownpart,
+ '| PAD 22',
+ '| LOCATE 17-22 /' || LEFT(tapevol,6) || '/',
+ '| STEM vols.'
+vmtrc = rc
+
+SELECT
+ WHEN ((vols.0 < 1) | (vmtrc = 28)) THEN DO /* No such internal volser found */
+ answer.1 = 'You have requested a mount for a tape with internal label' tapevol', however'
+ answer.2 = 'no such tape can be found belonging to' ownerid'. This MOUNT request has'
+ answer.3 = 'been cancelled. When mounting by internal label, the tape must belong to the'
+ answer.4 = 'ID requesting the mount. (For BATCH, that is the ID which scheduled the job.)'
+ answer.5 = 'The' vmtapeid 'LIST command may help you determine why this mount has failed. If'
+ answer.6 = 'you need further assistance with this, or if you do not know why you got'
+ answer.7 = 'this message, contact the ADN Computer Center.'
+ answer.0 = 7
+ finalrc = 6
+ END
+ WHEN (vmtrc <> 0) THEN DO /* Some VMTAPE error. */
+ 'PIPE STEM vols. | STEM answer.'
+ finalrc = vmtrc /* Issue VMTAPE's rc */
+ END
+ WHEN (vols.0 = 1) THEN DO /* This is goodness */
+ PARSE VAR vols.1 slotnum .
+ answer.0 = 0
+ finalrc = 0
+ END
+ OTHERWISE DO /* PANIC! Several found. Data integrity exposure. */
+ answer.1 = '***CAUTION*** User' ownerid 'owns more than one tape with internal label'
+ answer.2 = tapevol'. They are listed after this message. It is impossible to tell which is'
+ answer.3 = 'desired. This MOUNT has been cancelled to avoid the possibility of overwriting'
+ answer.4 = 'valuable data, or of reading the wrong data. To straighten this out, you'
+ answer.5 = 'should remove all the tapes listed below from the Computer Center tape'
+ answer.6 = 'racks, except for the one you want to use. The' vmtapeid 'LIST command may help'
+ answer.7 = 'you determine which you really want. You can also mount these tapes using'
+ answer.8 = 'the slot number to TAPEMAP them. If you need further assistance with'
+ answer.9 = 'this, or if you do not know why you got this message, contact the ADN'
+ answer.10 = 'Computer Center before proceeding.'
+ answer.11 = 'Slot--------Internal Label'
+ answer.0 = 11
+ 'PIPE STEM vols. | STEM answer. APPEND'
+ finalrc = 12
+ END
+END
+RETURN finalrc
diff --git a/vmworkshop-vmarcs/1996/sysoptap/qmount.test b/vmworkshop-vmarcs/1996/sysoptap/qmount.test
new file mode 100644
index 0000000..1fc4a94
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/sysoptap/qmount.test
@@ -0,0 +1,38 @@
+* This file contains most possible responses from VMTAPE. Uncomment the
+* one you want to test.
+VMTQRY061I There are no mounts pending.
+VMTQRY145I There are no allocations pending.
+*VMTMNT057A Mount LIBRARY volume 'SDA156' on 0881, NO RING, for BATCHA02 0181.
+*VMTMNT057A Mount LIBRARY volume 'VMD045' on 1881, NO RING, for VMBACKUP 0310.
+*VMTMNT057A Mount LIBRARY volume '000058' on 1888, NO RING, for U52983 0181.
+*VMTMNT057A Mount LIBRARY volume '000058' on 1889, RING IN, for U52983 0181.
+*VMTMNT057A Mount LIBRARY volume 'O01029' on 1882, RING IN, for U09046 0181.
+*VMTMNT057A Mount LIBRARY volume 'VMB510' on 1883, RING IN, for VMBACKUP 0320.
+*VMTDRV144I Waiting for a 18track XF BPI drive for VMBACKUP 0420 VXB510.
+*VMTXXXYYYI We're freaking out around here, and this long message to say it just proves it. I mean we can go on and o
+*VMTVOL699I MOUNT BATCHA02 0181 waiting for volume N09018.
+*VMTDRV144I Waiting for a 9-track 1600 BPI drive for BATCHA01 0181 N09018.
+*VMTVOL699I MOUNT BATCHA02 0181 waiting for volume N09018.
+*VMTDRV144I Waiting for a 9-track 1600 BPI drive for BATCHA01 0181 N09018.
+*VMTMNT057A Mount LIBRARY volume 'VMA270' on 1882, NO RING, for VMBACKUP 0310.
+*VMTMNT057A Mount LIBRARY volume 'VMA326' on 1888, NO RING, for VMBACKUP 0510.
+*VMTMNT057A Mount LIBRARY volume 'VMA333' on 1889, NO RING, for VMBACKUP 0710.
+*VMTVOL699I MOUNT BATCHA02 0181 waiting for volume N09018.
+*VMTVOL701I MOUNT VMBACKUP 0610 waiting for volume VMA326 which is mounted on 1888.
+*VMTDRV144I Waiting for a 9-track 1600 BPI drive for BATCHA01 0181 N09018.
+*VMTMNT057A Mount FOREIGN volume 'IL336' on 0880, NO RING, for U52983 0181.
+*VMTMNT057A Mount LIBRARY volume '000058' on 1889, RING IN, for U52983 0181.
+*VMTMNT057A Mount LIBRARY volume '000029' on 1882, RING IN, for U09046 0181.
+*VMTMNT057A Mount LIBRARY volume 'VMB510' on 1883, RING IN, for VMBACKUP 0320.
+*VMTMNT280A Mount selected COMPCTR SCRATCH volume 000342 on '188B', RING IN, for U52983 0181.
+*VMTDRV144I Waiting for a 18track XF BPI drive for VMBACKUP 0420 VXB510.
+*VMTVOL699I MOUNT BATCHA02 0181 waiting for volume N09018.
+*VMTDRV144I Waiting for a 9-track 1600 BPI drive for BATCHA01 0181 N09018.
+*VMTVOL699I MOUNT BATCHA02 0181 waiting for volume N09018.
+*VMTDRV144I Waiting for a 9-track 1600 BPI drive for BATCHA01 0181 N09018.
+*VMTMNT057A Mount LIBRARY volume 'VMA270' on 1882, NO RING, for VMBACKUP 0310.
+*VMTMNT057A Mount LIBRARY volume 'VMA326' on 1888, NO RING, for VMBACKUP 0510.
+*VMTMNT057A Mount LIBRARY volume 'VMA333' on 1889, NO RING, for VMBACKUP 0710.
+*VMTVOL699I MOUNT BATCHA02 0181 waiting for volume N09018.
+*VMTVOL701I MOUNT VMBACKUP 0610 waiting for volume VMA326 which is mounted on 1888.
+*VMTDRV144I Waiting for a 9-track 1600 BPI drive for BATCHA01 0181 N09018.
diff --git a/vmworkshop-vmarcs/1996/sysoptap/sysoptap.abstract b/vmworkshop-vmarcs/1996/sysoptap/sysoptap.abstract
new file mode 100644
index 0000000..691a6ef
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/sysoptap/sysoptap.abstract
@@ -0,0 +1,36 @@
+SYSOPTAP: a Disconnected Service Virtual for Displaying VMTAPE mounts
+
+ Version 1.2, May, 1996
+ Roger Deschner (rogerd@uic.edu)
+ University of Illinois at Chicago
+
+
+SYSOPTAP runs disconnected, and drives any number of ATTACHED or DIALED
+displays, showing in large letters the tapes to be mounted for VMTAPE.
+These displays are constantly updated, and so can be located in various
+areas to tell operators and tape librarians what is being requested.
+
+SYSOPTAP also intercepts FOREIGN tape mounts and assumes that they are
+for Standard Labeled tapes which are being mounted by their internal
+label name, such as from the DMSTVI exit. Then it looks these up in the
+TMC and shows the slot number for the operator to find that tape. Such
+tapes must belong to the user requesting the mount, due to the
+possibility that there may be more than one tape in the system with the
+same internal label name.
+
+SYSOPTAP REQUIRES:
+
+ Sterling VM:Tape (of course)
+ WAKEUP from CMS Utilities (can be replaced by HMF or PIPE)
+ CMS Pipelines 1.0106 (CMS8) or later
+
+See file SYSOPTAP INSTALL for installation details.
+
+Even if you have no interest in tape mounting, SYSOPTAP is a working
+example of a server which runs disconnected, and which drives displays
+connected to it using DEDICATE, ATTACH, or DIAL commands. These are
+driven using the CMS Pipelines FULLSCREEN device driver. This is a
+superior method, avoiding most of the problems of attempting to use a
+virtual machine's console as an informational display.
+
+Roger Deschner University of Illinois at Chicago R.Deschner@uic.edu
diff --git a/vmworkshop-vmarcs/1996/sysoptap/sysoptap.config b/vmworkshop-vmarcs/1996/sysoptap/sysoptap.config
new file mode 100644
index 0000000..8023396
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/sysoptap/sysoptap.config
@@ -0,0 +1,25 @@
+* This file defines the screens which will receive tape mount info
+*
+* Lines which begin with * are comments.
+* Each line defines one screen. It contains two tokens. First is the
+* Virtual address of the screen. Second is its size in lines, and
+* third is its width in columns.
+*
+* MODIFICATION HISTORY:
+* 06/06/94 - Roger - Add "foreign".
+* 05/10/94 - Roger - Change 4A0 from 32 lines to 24 lines.
+*
+4A0 24 80
+4A1 24 80
+4A2 24 80
+4B0 32 80
+4B1 32 80
+*
+* Following list of userids are allowed to mount 'FOREIGN', even if
+* the tape is not theirs.
+* FORMAT: FOREIGN
+*
+FOREIGN VMCENTER Operator
+FOREIGN U12860 Roger Deschner
+FOREIGN U11154 Gao, Social Science Data Archive
+FOREIGN U32472 Alan Hinds
\ No newline at end of file
diff --git a/vmworkshop-vmarcs/1996/sysoptap/sysoptap.conftest b/vmworkshop-vmarcs/1996/sysoptap/sysoptap.conftest
new file mode 100644
index 0000000..6a5c639
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/sysoptap/sysoptap.conftest
@@ -0,0 +1,19 @@
+* This file defines the screens which will receive tape mount info
+*
+* Lines which begin with * are comments.
+* Each line defines one screen. It contains three tokens. First is the
+* Virtual address of the screen. Second it its size in lines, and
+* third is its width in columns.
+*
+* MODIFICATION HISTORY:
+* 06/06/94 - Roger - TEST FILE
+*
+* Below line defines my own virtual console, for testing
+009 24 80
+*
+* Following list of userids are allowed to mount 'FOREIGN', even if
+* the tape is not theirs.
+* FORMAT: FOREIGN
+*
+FOREIGN U52983 Roger Deschner
+FOREIGN VMCENTER Operator
\ No newline at end of file
diff --git a/vmworkshop-vmarcs/1996/sysoptap/sysoptap.install b/vmworkshop-vmarcs/1996/sysoptap/sysoptap.install
new file mode 100644
index 0000000..e614a41
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/sysoptap/sysoptap.install
@@ -0,0 +1,53 @@
+SYSOPTAP Installation June 22, 1996
+
+To install SYSOPTAP, create a userid SYSOPTAP with a 1-cylinder 191 disk,
+and CP class BG. You can use a different userid if you wish, but if you
+do, you'll need to rename file SYSOPTAP CONFIG to be CONFIG.
+
+Load all the files to its A disk.
+
+Modify SYSOPTAP CONFIG to alter the list of displays which will be
+attached, their virtual addresses, and their physical screen dimensions.
+Also, alter the list of users who should be allowed to mount ANY foreign
+tape, not just their own. Ideally, there will be nobody in this list
+except system administration staff, as it represents a security hole.
+
+Give it OPERATOR privileges in VMTAPE.
+
+If you want to exploit its feature of looking up FOREIGN tape mounts, you
+should allow any user to mount a FOREIGN tape by NOT using the EXIT1 exit
+from VMTAPE. This can be done by removing/commenting out its line in
+VMTAPE CONFIG.
+
+Use PROP or HMF or VMTAPE CONFIG to route all VMTAPE mount messages to
+it.
+
+If you run batch jobs, review the code in QMOUNT EXEC which implements
+the UIC local convention that all batch jobs run with the ACIGROUP set to
+the user who submitted the job.
+
+Start it as a disconnected server. In your system startup (whether by HMF
+or AUTOLOG1) you will need to ATTACH the displays you always want to come
+up with every time. (Beware that you cannot ATTACH unless it is DISABLED,
+and that the CP DISABLE command runs asynchronously and takes about 5
+seconds to complete. This restriction makes the use of directory DEDICATE
+statements very difficult.)
+
+Otherwise, you can manually DIAL to it. When DIALing, if you have
+different screen sizes defined at different virtual addresses, you'll
+need to specify which you want to dial to. If the display is scrambled,
+you have defined the physical screen sizes wrong. To undial, flip the
+Test/Normal switch on the terminal. LDEVs work fine with DIAL to
+SYSOPTAP, as long as you are following the above rules about screen sizes
+matching.
+
+The QMOUNT EXEC which drives SYSOPTAP can be invoked with the (TEST
+option to display mounts from file QMOUNT TEST on your own terminal, so
+you can try it out without disrupting your normal operations.
+
+LEGAL NOTICE: SYSOPTAP is offered "as-is" without any warranty, including
+any warranty of merchantibility or fitness for a particular purpose.
+
+VM:Tape is a trade mark of Sterling Software Inc.
+
+Roger Deschner University of Illinois at Chicago rogerd@uic.edu
diff --git a/vmworkshop-vmarcs/1996/tar/README.md b/vmworkshop-vmarcs/1996/tar/README.md
new file mode 100644
index 0000000..beeb452
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/tar/README.md
@@ -0,0 +1,25 @@
+# CMS TAR
+
+Copyright 1995, Richard M. Troth, all rights reserved.
+
+This is the README file for CMS TAR 2.3 in plain text. There are some much needed enhancements to the translation (or not) selection logic that I had hoped to get in before the '96 Workshop, but time just seems to fly away.
+
+Copyright 1993, 1994, 1995, 1996 Richard M. Troth. This software may be freely distributed as long as copyright notice and header remain intact. There is no charge, although a substantial amount of personal time went into this effort.
+
+CMS TAR version 2 is a from-scratch 'tar' creator/extractor built upon the powerful CMS Pipelines. Files archived with CMS TAR version 2 can be extracted UNIX 'tar' and files archived with UNIX 'tar' can be extracted with CMS TAR.
+
+There also exists Rice CMS TAR version 1, which is C sourced and was written by Sean Starke. CMS TAR 1.0 is based on GNU TAR. This is NOT the same program or package. (although I have borrowed and modified Sean's HELP file) Changes to GNU TAR for CMS were returned to the Free Software Foundation. Extent of use or incorporation of those changes in the distribution GNU TAR package is unknown. Rice University DOES NOT SUPPORT THIS SOFTWARE. Don't ask them.
+
+Also included is a TARLIST EXEC. This tool allows you to "browse" a TAR archive and selectively view or extract files. It is NOT as functional as CMS FILELIST, which inspired it. Maybe more later.
+
+If you have any questions about this package, send e-mail to:
+
+ Rick Troth
+
+You can probably find updates at
+
+ http://casita.houston.tx.us/~troth/software/tar.vmarc
+ or
+ http://ua1vm.ua.edu/~troth/software/tar.vmarc
+
+
diff --git a/vmworkshop-vmarcs/1996/tar/a2e.rexx b/vmworkshop-vmarcs/1996/tar/a2e.rexx
new file mode 100644
index 0000000..33a6fd8
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/tar/a2e.rexx
@@ -0,0 +1,71 @@
+/* ----------------------------------------------------------------- ÆCS
+ * ASCII to EBCDIC and vice-versa code conversion tables.
+ * Tables included here are based on ASCII conforming to the ISO8859-1
+ * Latin 1 character set and EBCDIC conforming to the IBM Code Page 37
+ * Latin 1 character set (except for three pairs of characters in 037).
+ *
+ * Name: A2E REXX
+ * CMS Pipelines filter to translate ASCII to EBCDIC
+ * Author: Rick Troth, Houston, Texas, USA
+ * Date: 1992-Feb-27 for the filter, earlier for the table
+ *
+ * 1993-Aug-28: Thanks to Melinda Varian for helping me to
+ * correct some pipelining errors in this gem.
+ *
+ * Note: These tables are provided in source form so that you
+ * may modify them locally. I recommend that you not
+ * modify them just to make things look right on your
+ * screen. If you have an older terminal and there are
+ * not more than a dozen code-points that are wrong,
+ * then you're better off using CODEPAGE EXEC to set the
+ * CMS INPUT/OUTPUT translate tables. GOPHER EXEC
+ * *does respect* CMS' translate tables.
+ */
+
+ i = '000102030405060708090A0B0C0D0E0F'x
+ i = i || '101112131415161718191A1B1C1D1E1F'x
+ i = i || '202122232425262728292A2B2C2D2E2F'x
+ i = i || '303132333435363738393A3B3C3D3E3F'x
+ i = i || '404142434445464748494A4B4C4D4E4F'x
+ i = i || '505152535455565758595A5B5C5D5E5F'x
+ i = i || '606162636465666768696A6B6C6D6E6F'x
+ i = i || '707172737475767778797A7B7C7D7E7F'x
+ i = i || '808182838485868788898A8B8C8D8E8F'x
+ i = i || '909192939495969798999A9B9C9D9E9F'x
+ i = i || 'A0A1A2A3A4A5A6A7A8A9AAABACADAEAF'x
+ i = i || 'B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF'x
+ i = i || 'C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF'x
+ i = i || 'D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF'x
+ i = i || 'E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF'x
+ i = i || 'F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF'x
+
+ e = '00010203372D2E2F1605250B0C0D0E0F'x
+ e = e || '101112133C3D322618193F271C1D1E1F'x
+ e = e || '405A7F7B5B6C507D4D5D5C4E6B604B61'x
+ e = e || 'F0F1F2F3F4F5F6F7F8F97A5E4C7E6E6F'x
+ e = e || '7CC1C2C3C4C5C6C7C8C9D1D2D3D4D5D6'x
+ e = e || 'D7D8D9E2E3E4E5E6E7E8E9ADE0BD5F6D'x
+ e = e || '79818283848586878889919293949596'x
+ e = e || '979899A2A3A4A5A6A7A8A9C04FD0A107'x
+ e = e || '202122232415061728292A2B2C090A1B'x
+ e = e || '30311A333435360838393A3B04143EFF'x
+ e = e || '41AA4AB19FB26AB5BBB49A8AB0CAAFBC'x
+ e = e || '908FEAFABEA0B6B39DDA9B8BB7B8B9AB'x
+ e = e || '6465626663679E687471727378757677'x
+ e = e || 'AC69EDEEEBEFECBF80FDFEFBFCBAAE59'x
+ e = e || '4445424643479C485451525358555657'x
+ e = e || '8C49CDCECBCFCCE170DDDEDBDC8D8EDF'x
+
+/* ----------------------------------------------------------------- A2E
+ * Translate ASCII to EBCDIC.
+ */
+Do Forever
+ 'PEEKTO LINE'
+ If rc ^= 0 Then Leave
+ 'OUTPUT' Translate(line,e,i)
+ If rc ^= 0 Then Leave
+ 'READTO'
+ End /* Do While */
+
+Exit rc * (rc ^= 12)
+
diff --git a/vmworkshop-vmarcs/1996/tar/e2a.rexx b/vmworkshop-vmarcs/1996/tar/e2a.rexx
new file mode 100644
index 0000000..7dd2120
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/tar/e2a.rexx
@@ -0,0 +1,71 @@
+/* ----------------------------------------------------------------- ÆCS
+ * ASCII to EBCDIC and vice-versa code conversion tables.
+ * Tables included here are based on ASCII conforming to the ISO8859-1
+ * Latin 1 character set and EBCDIC conforming to the IBM Code Page 37
+ * Latin 1 character set (except for three pairs of characters in 037).
+ *
+ * Name: E2A REXX
+ * CMS Pipelines filter to translate EBCDIC to ASCII
+ * Author: Rick Troth, Houston, Texas, USA
+ * Date: 1992-Feb-27 for the filter, earlier for the table
+ *
+ * 1993-Aug-28: Thanks to Melinda Varian for helping me to
+ * correct some pipelining errors in this gem.
+ *
+ * Note: These tables are provided in source form so that you
+ * may modify them locally. I recommend that you not
+ * modify them just to make things look right on your
+ * screen. If you have an older terminal and there are
+ * not more than a dozen code-points that are wrong,
+ * then you're better off using CODEPAGE EXEC to set the
+ * CMS INPUT/OUTPUT translate tables. GOPHER EXEC
+ * *does respect* CMS' translate tables.
+ */
+
+ i = '000102030405060708090A0B0C0D0E0F'x
+ i = i || '101112131415161718191A1B1C1D1E1F'x
+ i = i || '202122232425262728292A2B2C2D2E2F'x
+ i = i || '303132333435363738393A3B3C3D3E3F'x
+ i = i || '404142434445464748494A4B4C4D4E4F'x
+ i = i || '505152535455565758595A5B5C5D5E5F'x
+ i = i || '606162636465666768696A6B6C6D6E6F'x
+ i = i || '707172737475767778797A7B7C7D7E7F'x
+ i = i || '808182838485868788898A8B8C8D8E8F'x
+ i = i || '909192939495969798999A9B9C9D9E9F'x
+ i = i || 'A0A1A2A3A4A5A6A7A8A9AAABACADAEAF'x
+ i = i || 'B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF'x
+ i = i || 'C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF'x
+ i = i || 'D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF'x
+ i = i || 'E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF'x
+ i = i || 'F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF'x
+
+ a = '000102039C09867F978D8E0B0C0D0E0F'x
+ a = a || '101112139D8508871819928F1C1D1E1F'x
+ a = a || '80818283840A171B88898A8B8C050607'x
+ a = a || '909116939495960498999A9B14159E1A'x
+ a = a || '20A0E2E4E0E1E3E5E7F1A22E3C282B7C'x
+ a = a || '26E9EAEBE8EDEEEFECDF21242A293B5E'x
+ a = a || '2D2FC2C4C0C1C3C5C7D1A62C255F3E3F'x
+ a = a || 'F8C9CACBC8CDCECFCC603A2340273D22'x
+ a = a || 'D8616263646566676869ABBBF0FDFEB1'x
+ a = a || 'B06A6B6C6D6E6F707172AABAE6B8C6A4'x
+ a = a || 'B57E737475767778797AA1BFD05BDEAE'x
+ a = a || 'ACA3A5B7A9A7B6BCBDBEDDA8AF5DB4D7'x
+ a = a || '7B414243444546474849ADF4F6F2F3F5'x
+ a = a || '7D4A4B4C4D4E4F505152B9FBFCF9FAFF'x
+ a = a || '5CF7535455565758595AB2D4D6D2D3D5'x
+ a = a || '30313233343536373839B3DBDCD9DA9F'x
+
+/* ----------------------------------------------------------------- E2A
+ * Translate EBCDIC to ASCII.
+ */
+Do Forever
+ 'PEEKTO LINE'
+ If rc ^= 0 Then Leave
+ 'OUTPUT' Translate(line,a,i)
+ If rc ^= 0 Then Leave
+ 'READTO'
+ End /* Do While */
+
+Exit rc * (rc ^= 12)
+
diff --git a/vmworkshop-vmarcs/1996/tar/executar.xedit b/vmworkshop-vmarcs/1996/tar/executar.xedit
new file mode 100644
index 0000000..ffbcab8
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/tar/executar.xedit
@@ -0,0 +1,70 @@
+/* REXX */
+
+Parse Upper Arg l .
+Parse Arg . c
+
+Select /* l */
+
+ When l = "" Then Do
+ 'COMMAND EXTRACT/LINE'
+ 'COMMAND CURSOR CMDLINE'
+ 'LOCATE :' || line.1
+ End /* When .. Do */
+
+ When l = "CURSOR" Then Do
+ 'COMMAND EXTRACT/LINE/CURSOR'
+ If cursor.3 = -1 Then Do
+ Address "COMMAND" 'XMITMSG 561 (CALLER TAR VAR'
+ Do i = 1 to message.0; 'EMSG' message.i; End
+ Exit 3
+ End
+ Call EXECUTAR cursor.3 c
+ 'LOCATE :' || line.1
+ End /* When .. Do */
+
+ When l = '*' Then Do
+ 'COMMAND EXTRACT/LINE/SIZE'
+ Do i = line.1 to size.1
+ Call EXECUTAR i c
+ End /* Do For */
+ 'LOCATE :' || line.1
+ End /* When .. Do */
+
+ End /* Select l */
+
+Exit
+
+
+
+EXECUTAR: Procedure
+Parse Arg l c
+
+'LOCATE :' || l
+'COMMAND EXTRACT/CURLINE'
+Parse Var curline.3 81 skip name
+
+c2 = ""
+Do While c ^= ""
+ Parse Var c c1 c
+ If c1 = '/' Then c1 = name
+ If c1 = '/N' Then c1 = name
+ If c1 = '/S' Then c1 = skip
+ c2 = c2 c1
+ End /* Do While */
+c = c2
+
+Parse Var c c1 c2
+Select /* c1 */
+ When c1 = "X" Then
+ 'COMMAND CMS PIPE' xc c2 '| CONSOLE'
+ When c1 = "EXTRACT" Then
+ 'COMMAND CMS PIPE' xc c2 '| CONSOLE'
+ When c1 = "PEEK" Then
+ 'COMMAND CMS PIPE' xc c2 '(PEEK | CONSOLE'
+ Otherwise 'COMMAND CMS' c
+ End /* Select c1 */
+
+'COMMAND REPLACE *' || Substr(curline.3,2)
+
+Return
+
diff --git a/vmworkshop-vmarcs/1996/tar/maketext.rexx b/vmworkshop-vmarcs/1996/tar/maketext.rexx
new file mode 100644
index 0000000..b471c48
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/tar/maketext.rexx
@@ -0,0 +1,227 @@
+/* © Copyright 1994, Richard M. Troth, all rights reserved.
+ *
+ * Name: MAKETEXT REXX
+ * VM TCP/IP Network Client and Server text converter
+ * Inspired by GOPCLITX, DROPDOTS, and other gems.
+ * Renamed from WEBTEXT because it's ubiquitous.
+ * Author: Rick Troth, Houston, Texas, USA
+ * Date: 1994-Feb-27, 1994-Oct-15
+ *
+ * Replaces: A2E, E2A, TCPA2E, TCPE2A
+ */
+
+/* ----------------------------------------------------------------- ÆCS
+ * ASCII to EBCDIC and vice-versa code conversion tables.
+ * Tables included here are based on ASCII conforming to the ISO8859-1
+ * Latin 1 character set and EBCDIC conforming to the IBM Code Page 37
+ * Latin 1 character set (except for three pairs of characters in 037).
+ */
+
+Parse Upper Arg mode code . '(' . ')' .
+If mode = "" Then mode = "LOCAL"
+
+ i = '000102030405060708090A0B0C0D0E0F'x
+ i = i || '101112131415161718191A1B1C1D1E1F'x
+ i = i || '202122232425262728292A2B2C2D2E2F'x
+ i = i || '303132333435363738393A3B3C3D3E3F'x
+ i = i || '404142434445464748494A4B4C4D4E4F'x
+ i = i || '505152535455565758595A5B5C5D5E5F'x
+ i = i || '606162636465666768696A6B6C6D6E6F'x
+ i = i || '707172737475767778797A7B7C7D7E7F'x
+ i = i || '808182838485868788898A8B8C8D8E8F'x
+ i = i || '909192939495969798999A9B9C9D9E9F'x
+ i = i || 'A0A1A2A3A4A5A6A7A8A9AAABACADAEAF'x
+ i = i || 'B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF'x
+ i = i || 'C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF'x
+ i = i || 'D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF'x
+ i = i || 'E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF'x
+ i = i || 'F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF'x
+
+If code ^= "" Then Do
+ 'CALLPIPE DISK' code 'TCPXLBIN | STEM XLT.'
+ If rc ^= 0 | xlt.0 < 3 Then code = ""
+ End /* If .. Do */
+
+Select /* mode */
+ When Abbrev("LOCAL",mode,3) Then Call LOCAL
+ When Abbrev("LCL",mode,3) Then Call LOCAL
+ When Abbrev("EBCDIC",mode,1) Then Call LOCAL
+ When Abbrev("NETWORK",mode,3) Then Call NETWORK
+ When Abbrev("ASCII",mode,1) Then Call NETWORK
+ When Abbrev("DOTTED",mode,3) Then Call DOTTED
+ When Abbrev("UNIX",mode,1) Then Call UNIX
+ Otherwise Do
+ Address "COMMAND" 'XMITMSG 3 MODE (ERRMSG'
+ rc = 24
+ End /* Otherwise Do */
+ End /* Select mode */
+
+Exit rc * (rc ^= 12)
+
+
+/* --------------------------------------------------------------- LOCAL
+ * Input: raw ASCII text
+ * Output: plain (EBCDIC) text
+ */
+LOCAL:
+
+'ADDPIPE *.OUTPUT: | STRIP TRAILING 0D | PAD 1 | *.OUTPUT:'
+If rc ^= 0 Then Return
+
+If code = "" Then Do /* use the standard table */
+ e = '00010203372D2E2F1605250B0C0D0E0F'x
+ e = e || '101112133C3D322618193F271C1D1E1F'x
+ e = e || '405A7F7B5B6C507D4D5D5C4E6B604B61'x
+ e = e || 'F0F1F2F3F4F5F6F7F8F97A5E4C7E6E6F'x
+ e = e || '7CC1C2C3C4C5C6C7C8C9D1D2D3D4D5D6'x
+ e = e || 'D7D8D9E2E3E4E5E6E7E8E9ADE0BD5F6D'x
+ e = e || '79818283848586878889919293949596'x
+ e = e || '979899A2A3A4A5A6A7A8A9C04FD0A107'x
+ e = e || '202122232415061728292A2B2C090A1B'x
+ e = e || '30311A333435360838393A3B04143EFF'x
+ e = e || '41AA4AB19FB26AB5BBB49A8AB0CAAFBC'x
+ e = e || '908FEAFABEA0B6B39DDA9B8BB7B8B9AB'x
+ e = e || '6465626663679E687471727378757677'x
+ e = e || 'AC69EDEEEBEFECBF80FDFEFBFCBAAE59'x
+ e = e || '4445424643479C485451525358555657'x
+ e = e || '8C49CDCECBCFCCE170DDDEDBDC8D8EDF'x
+ End /* If .. Do */
+Else e = xlt.2
+
+buff = ""
+Do Forever
+
+ 'PEEKTO DATA'
+ If rc ^= 0 Then Leave
+
+ buff = buff || data
+ Do While Index(buff,'0A'x) > 0
+ Parse Var buff line '0A'x buff
+ 'OUTPUT' Translate(line,e,i)
+ If rc ^= 0 Then Leave
+ End /* Do While */
+ If rc ^= 0 Then Leave
+
+ 'READTO'
+ If rc ^= 0 Then Leave
+
+ End /* Do Forever */
+
+If buff ^= "" Then 'OUTPUT' Translate(buff,e,i)
+
+Return
+
+
+/* ------------------------------------------------------------- NETWORK
+ * Input: plain (EBCDIC) text
+ * Output: raw ASCII byte stream
+ */
+NETWORK:
+
+'ADDPIPE *.OUTPUT: | SPEC 1-* 1 x0D0A NEXT | *.OUTPUT:'
+If rc ^= 0 Then Return
+
+If code = "" Then Do /* use the standard table */
+ a = '000102039C09867F978D8E0B0C0D0E0F'x
+ a = a || '101112139D8508871819928F1C1D1E1F'x
+ a = a || '80818283840A171B88898A8B8C050607'x
+ a = a || '909116939495960498999A9B14159E1A'x
+ a = a || '20A0E2E4E0E1E3E5E7F1A22E3C282B7C'x
+ a = a || '26E9EAEBE8EDEEEFECDF21242A293B5E'x
+ a = a || '2D2FC2C4C0C1C3C5C7D1A62C255F3E3F'x
+ a = a || 'F8C9CACBC8CDCECFCC603A2340273D22'x
+ a = a || 'D8616263646566676869ABBBF0FDFEB1'x
+ a = a || 'B06A6B6C6D6E6F707172AABAE6B8C6A4'x
+ a = a || 'B57E737475767778797AA1BFD05BDEAE'x
+ a = a || 'ACA3A5B7A9A7B6BCBDBEDDA8AF5DB4D7'x
+ a = a || '7B414243444546474849ADF4F6F2F3F5'x
+ a = a || '7D4A4B4C4D4E4F505152B9FBFCF9FAFF'x
+ a = a || '5CF7535455565758595AB2D4D6D2D3D5'x
+ a = a || '30313233343536373839B3DBDCD9DA9F'x
+ End /* If .. Do */
+Else a = xlt.3
+
+Do Forever
+
+ 'PEEKTO LINE'
+ If rc ^= 0 Then Leave
+
+ 'OUTPUT' Translate(line,a,i)
+ If rc ^= 0 Then Leave
+
+ 'READTO'
+ If rc ^= 0 Then Leave
+
+ End /* Do Forever */
+
+Return
+
+
+/* -------------------------------------------------------------- DOTTED
+ * Input: plain (EBCDIC) text
+ * Output: ASCII byte stream terminated by CR/LF/./CR/LF
+ */
+DOTTED:
+
+Call NETWORK
+
+'OUTPUT' Translate('.',a,i)
+
+Return
+
+
+/*
+ * variables:
+ * xlt.0 should be "3", meaning three records read
+ * xlt.1 should be a comment
+ * xlt.2 should be our ASCII ---> EBCDIC table
+ * xlt.3 should be our EBCDIC ---> ASCII table
+ * i is set to the dummy input table
+ */
+
+
+/* ---------------------------------------------------------------- UNIX
+ * Input: plain (EBCDIC) text
+ * Output: ASCII byte stream with UNIX line convention (NL)
+ */
+UNIX:
+
+'ADDPIPE *.OUTPUT: | SPEC 1-* 1 x0A NEXT | *.OUTPUT:'
+If rc ^= 0 Then Return
+
+If code = "" Then Do /* use the standard table */
+ a = '000102039C09867F978D8E0B0C0D0E0F'x
+ a = a || '101112139D8508871819928F1C1D1E1F'x
+ a = a || '80818283840A171B88898A8B8C050607'x
+ a = a || '909116939495960498999A9B14159E1A'x
+ a = a || '20A0E2E4E0E1E3E5E7F1A22E3C282B7C'x
+ a = a || '26E9EAEBE8EDEEEFECDF21242A293B5E'x
+ a = a || '2D2FC2C4C0C1C3C5C7D1A62C255F3E3F'x
+ a = a || 'F8C9CACBC8CDCECFCC603A2340273D22'x
+ a = a || 'D8616263646566676869ABBBF0FDFEB1'x
+ a = a || 'B06A6B6C6D6E6F707172AABAE6B8C6A4'x
+ a = a || 'B57E737475767778797AA1BFD05BDEAE'x
+ a = a || 'ACA3A5B7A9A7B6BCBDBEDDA8AF5DB4D7'x
+ a = a || '7B414243444546474849ADF4F6F2F3F5'x
+ a = a || '7D4A4B4C4D4E4F505152B9FBFCF9FAFF'x
+ a = a || '5CF7535455565758595AB2D4D6D2D3D5'x
+ a = a || '30313233343536373839B3DBDCD9DA9F'x
+ End /* If .. Do */
+Else a = xlt.3
+
+Do Forever
+
+ 'PEEKTO LINE'
+ If rc ^= 0 Then Leave
+ If line = " " Then line = ""
+
+ 'OUTPUT' Translate(line,a,i)
+ If rc ^= 0 Then Leave
+
+ 'READTO'
+ If rc ^= 0 Then Leave
+
+ End /* Do Forever */
+
+Return
+
diff --git a/vmworkshop-vmarcs/1996/tar/proftlst.xedit b/vmworkshop-vmarcs/1996/tar/proftlst.xedit
new file mode 100644
index 0000000..e54c3af
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/tar/proftlst.xedit
@@ -0,0 +1,74 @@
+/*
+ * Name: PROFTLST XEDIT
+ * Profile macro for TARLIST
+ * Copyright 1992, Richard M. Troth
+ */
+
+Parse Arg fn ft fm . '(' . ')' tfn tft tfm . '(' . ')' .
+Select
+ When tft ^= "" Then Do
+ lcmd = '<' tfn tft tfm '| tar tf - (TARLIST'
+ xcmd = '<' tfn tft tfm '| tar xf -'
+ End /* When .. Do */
+ When Datatype(tfn,'N') Then Do
+ lcmd = 'tar ts' tfn '(TARLIST'
+ xcmd = 'tar xs' tfn
+ End /* When .. Do */
+ When Left(tfn,3) = "TAP" & Datatype(Right(tfn,1),'N') ,
+ Length(tfn) = 4 Then Do
+ lcmd = 'tar t' || Right(tfn,1) '(TARLIST'
+ xcmd = 'tar x' || Right(tfn,1)
+ End /* When .. Do */
+ Otherwise Do
+ lcmd = 'tar tf' tfn '(TARLIST'
+ xcmd = 'tar xf' tfn
+ End /* Otherwise Do */
+ End /* Select */
+
+'COMMAND CMS GLOBALV SELECT TARLIST PUT XCMD'
+
+/* 'COMMAND SET CMDLINE BOTTOM' */
+'COMMAND SET SCALE OFF'
+'COMMAND SET PREFIX OFF'
+'COMMAND SET RESERVED 2 HIGH Cmd Filename Bytes Date Time'
+'COMMAND SET RESERVED -5 HIGH 1= Help 2= Refresh 3= Quit 4= Sort(name) 5= Sort(date) 6= Sort(size)'
+'COMMAND SET RESERVED -4 HIGH 7= Backward 8= Forward 9= Extract 10= 11= Peek 12='
+'COMMAND SET MSGLINE ON -3'
+'COMMAND SET CURLINE ON 3'
+'COMMAND SET TOFEOF OFF'
+'COMMAND SET ENTER IGNORE MACRO EXECUTAR'
+
+'COMMAND SET CASE MIXED IGNORE'
+
+'COMMAND SET RECFM V'
+'COMMAND SET LRECL *'
+'COMMAND SET TRUNC *'
+
+'COMMAND SET SYNONYM LINEND / REFRESH' ,
+ 'COMMAND PRES /' ,
+ 'COMMAND SET MSGMODE OFF /' ,
+ 'COMMAND :1 /' ,
+ 'COMMAND DELETE * /' ,
+ 'COMMAND CMS PIPE' lcmd '| XEDIT' fn ft fm '/' ,
+ 'COMMAND :1 /' ,
+ 'COMMAND REST'
+'REFRESH'
+'COMMAND SET ALT 0'
+
+'COMMAND SET PF1 HELP CMS TARLIST'
+'COMMAND SET PF2 REFRESH#COMMAND CURSOR COLUMN'
+'COMMAND SET PF3 QQUIT'
+
+'COMMAND SET PF4 SORT * A 7 52' /* SORT(NAME) */
+'COMMAND SET PF5 SORT * D 69 70 63 67 72 79' /* SORT(DATE) */
+'COMMAND SET PF6 SORT * D 54 61' /* SORT(SIZE) */
+/* asis 'SORT * D 81 88' */
+'COMMAND SET PF9 MACRO EXECUTAR CURSOR PIPE' xcmd '/ (SKIP /S'
+'COMMAND SET PF10'
+'COMMAND SET PF11 MACRO EXECUTAR CURSOR PIPE' xcmd '/ (SKIP /S PEEK'
+'COMMAND SET PF12'
+
+'COMMAND CURSOR COLUMN'
+
+Exit
+
diff --git a/vmworkshop-vmarcs/1996/tar/tar.copyrigh b/vmworkshop-vmarcs/1996/tar/tar.copyrigh
new file mode 100644
index 0000000..920df05
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/tar/tar.copyrigh
@@ -0,0 +1,22 @@
+� Copyright 1993, 1995, 1996
+ Richard M. Troth, all rights reserved.
+
+
+ This software may be freely distributed provided that this notice
+ and the code itself remain unchanged. The user (or programmer)
+ may make any private changes needed to accomodate local conventions.
+ This software is provided AS-IS with NO WARRANTY. Neither the author
+ (Richard M. Troth) nor any of his employers, supporters, or dependents
+ shall be held liable for any damages resulting from the use of this software.
+
+ If you wish to support future development of this software,
+ contact the author:
+
+ Rick Troth
+ Houston, Texas 77065 USA
+ troth@casita.houston.tx.us
+
+ This software depends on other support programs, of which some
+ are freely distributable, others proprietary and/or licensed.
+
+
diff --git a/vmworkshop-vmarcs/1996/tar/tar.exec b/vmworkshop-vmarcs/1996/tar/tar.exec
new file mode 100644
index 0000000..b6048a3
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/tar/tar.exec
@@ -0,0 +1,12 @@
+/* © Copyright 1992, 1996, Richard M. Troth, all rights reserved.
+ * (casita sourced)
+ *
+ * Name: TAR EXEC
+ * run CMS TAR from the command line
+ */
+
+Parse Arg argstring
+'pipe tar' argstring
+Exit rc
+
+
diff --git a/vmworkshop-vmarcs/1996/tar/tar.filelist b/vmworkshop-vmarcs/1996/tar/tar.filelist
new file mode 100644
index 0000000..d5477db
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/tar/tar.filelist
@@ -0,0 +1,35 @@
+* © Copyright 1996, Richard M. Troth, all rights reserved.
+*
+* Name: TAR FILELIST
+* this lists the files comprising CMS TAR v2 r3 m1
+*
+ TAR FILELIST * README "this file" 0
+ TAR README *
+ TAR COPYRIGHT *
+*
+* CMS TAR:
+ TAR EXEC *
+ TAR REXX *
+ TARINDEX REXX *
+ TARREADC REXX *
+ TARPUNCH REXX *
+* TARTAPER REXX *
+* TARTAPEW REXX *
+ TARTAKE REXX *
+*
+* RXVMGROUP MODULE *
+* RXVMGROUP HELPCMS *
+* RXVMGROUP ASSEMBLE *
+*
+ A2E REXX *
+ E2A REXX *
+ MAKETEXT REXX *
+*
+ TAR HELPCMS *
+*
+* TARLIST:
+ TARLIST EXEC *
+ PROFTLST XEDIT *
+ EXECUTAR XEDIT *
+ TARLIST HELPCMS *
+*
diff --git a/vmworkshop-vmarcs/1996/tar/tar.helpcms b/vmworkshop-vmarcs/1996/tar/tar.helpcms
new file mode 100644
index 0000000..e0a93d6
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/tar/tar.helpcms
@@ -0,0 +1,73 @@
+.cm This file was created from file "TAR -HLPCMS"
+.cm using version 1989-09-06 of the UCSF "HELPCONV" command.
+.cm
+.cm Author of this help file: Sean Starke
+.cm
+.cs 1 on
+
+¢|TAR EXEC¢%
+
+ Use CMS TAR to store many files in an archive file on tape or disk.
+ CMS TAR can read UNIX TAR archives and archives created with CMS TAR
+ can be read by UNIX 'tar'.
+
+.cs 1 off
+.cs 2 on
+ The format of the TAR command is:
+ +------------+-------------------------------------------------------------+
+ | | |
+ | TAR | command [tar-file] [pattern] [(options...] |
+ | | |
+ +------------+-------------------------------------------------------------+
+
+.cs 2 off
+.cs 3 on
+¢|where:¢%
+
+¢|command¢% is a combination of C, X, or T with M, V and F or 0..7,
+ C|X|T[M][V][S|F|0|1|2|3|4|5|6|7], signifying:
+
+ C : create a new archive
+ X : extract files from an archive
+ T : list the files in an archive
+ (these are mutually exclusive)
+
+ M : don't extract modification times
+ V : verbose
+
+ S : working with a spool file
+ F : next argument is a disk resident archive
+ 01234567 : tape drive number, default is TAP1 (181)
+ (F, S, and numerics are mutually exclusive)
+
+¢|tar-file¢% is the filename of the disk resident archive, having a filetype of
+ TAR, if F was specified in the command. This can also be the
+ spoolid of the file when extracting a spool TAR file, or a target
+ user@node when creating a spool TAR file.
+
+¢|pattern¢% specifies what files to tar/untar.
+ CREATING AN ARCHIVE: When creating an archive, this pattern must
+ be specified in the form 'fn ft fm', like when using the LISTSILE
+ command. In addition to '*' and '%', periods (.) can be used as
+ wilcards (like in Unix) so that if you specify simply '.' as the
+ pattern, then all files on your 'A' disk will be archived. If the
+ pattern omitted, only files specified with the INCLUDE option will
+ be archived.
+ EXTRACTING AN ARCHIVE. If you want to extract only one file, put
+ it's name (exactly as it appears with the LIST command) here. If
+ this option is omitted, all files will be extracted from the
+ archive.
+
+.cs 3 off
+.cs 4 on
+¢|Options:¢%
+
+¢|INCLUDE fn
+ specifies a FILELIST file listing files to be included in an
+ archive being built. When building an archive file according to
+ the INCLUDE file, the name of each file as it will appear in the
+ archive may follow fn ft fm in the FILELIST, overriding the
+ default.
+
+.cs 4 off
+
diff --git a/vmworkshop-vmarcs/1996/tar/tar.rexx b/vmworkshop-vmarcs/1996/tar/tar.rexx
new file mode 100644
index 0000000..ac8aacb
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/tar/tar.rexx
@@ -0,0 +1,689 @@
+/* © Copyright 1992, 1995, Richard M. Troth, all rights reserved.
+ * (casita sourced)
+ *
+ * Name: TAR REXX
+ * a from-scratch replacement for CMS 'tar' v1
+ * Author: Rick Troth, Houston, Texas, USA
+ */
+
+vrm = "2.3.0"
+
+dfmt = 'U' /* date format used by LISTFILE */
+tzoh = -6 /* timezone offset in hours */
+tzoh = tzoffset('H')
+tzos = tzoh * 60 * 60 /* timezone offset in seconds */
+tzos = tzoffset('S')
+
+/* ASCII non-printables */
+a_nprint = '00010203040506FF'x
+/* EBCDIC non-printables */
+e_nprint = '0001020304FF'x
+
+Parse Arg cmd args '(' opts ')' .
+Upper cmd opts
+Parse Source . . . . . arg0 .
+argo = arg0 || ':'
+
+tc = "" /* primary operation (tar command) */
+tf = "" /* archive file (tar file) */
+td = "" /* archive device (disk, tape, or SPOOL) */
+
+verbose = 0; modtime = 1; prompt = 0
+include = ""; tarlist = 0; skip = 0
+peek = 0; once = 0; replace = 0
+
+Do While cmd ^= ""
+ Parse Var cmd 1 c 2 cmd
+ Select /* c */
+ When c = '-' Then nop
+ When c = 'C' Then Do
+ If tc ^= "" Then Do
+ Address "COMMAND" 'XMITMSG 66 TC C (ERRMSG'
+ Say argo "multiple primary operations."
+ Exit 24
+ End /* If .. Do */
+ tc = c
+ End /* When Do */
+ When c = 'X' Then Do
+ If tc ^= "" Then Do
+ Say argo "multiple primary operations."
+ Exit 24
+ End /* If .. Do */
+ tc = c
+ End /* When Do */
+ When c = 'T' Then Do
+ If tc ^= "" Then Do
+ Say argo "multiple primary operations."
+ Exit 24
+ End /* If .. Do */
+ tc = c
+ End /* When Do */
+ When c = 'R' Then Do
+ If tc ^= "" Then Do
+ Say argo "multiple primary operations."
+ Exit 24
+ End /* If .. Do */
+ tc = c
+ End /* When Do */
+ When c = 'F' Then Do
+ If tf ^= "" Then Do
+ Say argo "multiple archives specified."
+ Exit 24
+ End /* If .. Do */
+ Parse Var args tf args
+ td = 'F'
+ End /* When Do */
+ When c = 'S' Then Do
+ If tf ^= "" Then Do
+ Say argo "multiple archives specified."
+ Exit 24
+ End /* If .. Do */
+ Parse Var args tf args
+ td = 'S'
+ End /* When Do */
+ When c = '0' | c = '1' | c = '2' | c = '3' ,
+ c = '4' | c = '5' | c = '6' | c = '7' Then Do
+ If tf ^= "" Then Do
+ Say argo "multiple archives specified."
+ Exit 24
+ End /* If .. Do */
+ tf = "TAP" || c
+ td = 'T'
+ End /* When Do */
+ When c = 'V' Then Do
+ verbose = 1
+ Say "CMS TAR - Version" vrm "(piped)"
+ End /* When Do */
+ When c = 'M' Then modtime = 0
+ When c = 'W' Then prompt = 1
+ Otherwise Do
+ Address "COMMAND" 'XMITMSG 3 C (ERRMSG'
+ Say argo "unrecognized command token" c
+ Exit 24
+ End /* Otherwise Do */
+ End /* Select c */
+ End
+
+If tf = "" Then tf = "TAP1"
+If td = "" Then td = "T"
+
+Do While opts ^= ""
+ Parse Var opts op opts
+ Select /* op */
+ When Abbrev("TARLIST",op,4) Then tarlist = 1
+ When Abbrev("NOTARLIST",op,3) Then tarlist = 0
+ When Abbrev("INCLUDE",op,3) Then Parse Var opts include opts
+ When Abbrev("SKIP",op,1) Then Parse Var opts skip opts
+ When Abbrev("PEEK",op,2) Then Do; peek = 1; once = 1; End
+ When Abbrev("ONCE",op,1) Then once = 1
+ When Abbrev("VERBOSE",op,1) Then verbose = 1
+ When Abbrev("TERSE",op,5) Then verbose = 0
+ When Abbrev("MODTIME",op,1) Then modtime = 1
+ When Abbrev("NOMODTIME",op,3) Then modtime = 0
+ When Abbrev("PROMPT",op,2) Then prompt = 1
+ When Abbrev("NOPROMPT",op,3) Then prompt = 0
+ When Abbrev("REPLACE",op,3) Then replace = 1
+ When Abbrev("NOREPLACE",op,3) Then replace = 0
+ Otherwise Do
+ Address "COMMAND" 'XMITMSG 3 OP (ERRMSG'
+/* Say argo "unrecognized option" op */
+ Exit 24
+ End /* Otherwise Do */
+ End /* Select op */
+ End /* Do While */
+
+Select /* tc */
+
+ When tc = 'C' Then Do
+ Select /* td */
+ When td = 'F' Then Do
+ If tf ^= "-" Then Do
+ Parse Var tf tfn '.' tft '.' tfm '.' .
+ If tft = "" Then tft = "TAR"
+ If tfm = "" Then tfm = "A"
+ 'ADDPIPE *.OUTPUT: | >' tfn tft tfm 'F 512'
+ End /* If .. Do */
+ Call CREATE
+ End /* When .. Do */
+ When td = 'T' Then Do
+ 'ADDPIPE *.OUTPUT: | TAPE' tf
+ Call CREATE
+ End /* When .. Do */
+ When td = 'S' Then Do
+ 'ADDPIPE *.OUTPUT: | TARPUNCH' tf
+ Call CREATE
+ End /* When .. Do */
+ Otherwise Do
+ Say argo "internal error: unknown TAR target" td tf
+ End /* Otherwise Do */
+ End /* Select td */
+ End /* When .. Do */
+
+ When tc = 'X' Then Do
+ Select /* td */
+ When td = 'F' Then Do
+ If tf ^= "-" Then Do
+ Parse Var tf tfn '.' tft '.' tfm '.' .
+ If tft = "" Then tft = "TAR"
+ 'ADDPIPE <' tfn tft tfm '| *.INPUT:'
+ End /* If .. Do */
+ Call XTRACT
+ End /* When .. Do */
+ When td = 'T' Then Do
+ 'CALLPIPE CMS TAPE REW (' tf /* not quite right */
+ 'ADDPIPE TAPE' tf '| *.INPUT:'
+ Call XTRACT
+ 'CALLPIPE CMS TAPE REW (' tf /* not quite right */
+ End /* When .. Do */
+ When td = 'S' Then Do
+ 'ADDPIPE TARREADC' tf '| *.INPUT:'
+ Call XTRACT
+ End /* When .. Do */
+ Otherwise Do
+ Say argo "internal error: unknown TAR source" td tf
+ End /* Otherwise Do */
+ End /* Select td */
+ End /* When .. Do */
+
+ When tc = 'T' Then Do
+ Select /* td */
+ When td = 'F' Then Do
+ If tf ^= "-" Then Do
+ Parse Var tf tfn '.' tft
+ If tft = "" Then tft = "TAR"
+ 'ADDPIPE <' tfn tft '| *.INPUT:'
+ End /* If .. Do */
+ Call LISTOC
+ End /* When .. Do */
+ When td = 'T' Then Do
+ 'CALLPIPE CMS TAPE REW (' tf /* not quite right */
+ 'ADDPIPE TAPE' tf '| *.INPUT:'
+ Call LISTOC
+ 'CALLPIPE CMS TAPE REW (' tf /* not quite right */
+ End /* When .. Do */
+ When td = 'S' Then Do
+ 'ADDPIPE TARREADC' tf '| *.INPUT:'
+ Call LISTOC
+ End /* When .. Do */
+ Otherwise Do
+ Say argo "internal error: unknown TAR source" td tf
+ End /* Otherwise Do */
+ End /* Select td */
+ End /* When .. Do */
+
+ End /* Select tc */
+
+Exit rc * (rc ^= 12)
+
+
+/* ---------------------------------------------------------------------
+ * create or update
+ */
+CREATE:
+
+If include = "" Then 'ADDPIPE TARINDEX' args '| *.INPUT:'
+ Else 'ADDPIPE <' include 'FILELIST | *.INPUT:'
+
+userid = Userid()
+groupid = "N/A" /* vmgroup(userid) */
+'CALLPIPE VAR USERID | XLATE LOWER | VAR USERID'
+'CALLPIPE VAR GROUPID | XLATE LOWER | VAR GROUPID'
+
+Do Forever
+
+ 'READTO RECORD'
+ If rc ^= 0 Then Leave
+ If Strip(record) = "" Then Iterate
+
+ If Left(record,1) = '*' Then Iterate
+ If Left(record,1) = '#' Then Iterate
+
+/*
+ Parse Upper Var record fn ft fm . '(' opts ')' .
+ Parse Var record . . . name '(' . ')' .
+ */
+ q1 = Index(record,"'"); q2 = Index(record,'"')
+ Select
+ When q1 = 0 & q2 = 0 Then ,
+ Parse Var record fn ft fm name '05'x . '05'x type '05'x .
+ When q1 = 0 Then ,
+ Parse Var record fn ft fm name '"' . '"' type
+ When q2 = 0 Then ,
+ Parse Var record fn ft fm name "'" . "'" type
+ When q1 > q2 Then ,
+ Parse Var record fn ft fm name '"' . '"' type
+ When q2 > q1 Then ,
+ Parse Var record fn ft fm name "'" . "'" type
+ End /* Select */
+ Upper fn ft fm
+ Parse Var name . '(' opts ')' .
+ name = Strip(name)
+ If name = "" Then ,
+ 'CALLPIPE LITERAL' Strip(fn) || '.' || Strip(ft) ,
+ '| XLATE LOWER | STRIP | VAR NAME'
+ 'CALLPIPE COMMAND LISTFILE' fn ft fm '(DATE | DROP | VAR FILESPEC'
+ Parse Var filespec . . fmode recfm lrecl . . date time .
+ fmode = Right(fmode,1)
+ Select /* fmode */
+ When fmode = 0 Then perm = '600'
+ When fmode = 1 Then perm = '644'
+ When fmode = 2 Then perm = '644'
+ When fmode = 3 Then perm = '444'
+ When fmode = 4 Then perm = '644'
+ When fmode = 5 Then perm = '644'
+ When fmode = 6 Then perm = '666'
+ Otherwise perm = '644'
+ End /* Select fmode */
+
+ If recfm = 'V' Then lrecl = 0
+
+ 'CALLPIPE <' fn ft fm '| TAKE FIRST 1 | VAR SAMPLE'
+ If Verify(sample,e_nprint,'M') = 0 Then trans = 't'
+ Else trans = 'b'
+
+ Select
+ When trans = 't' Then
+ pipe = '| STRIP TRAILING | E2A | SPEC 1-* 1 .0A. X2C NEXT'
+ When lrecl = 0 Then
+ pipe = '| BLOCK 512 CMS'
+ Otherwise
+ pipe = ""
+ End /* Select */
+
+ 'CALLPIPE <' fn ft fm pipe '| COUNT BYTES | VAR SIZE'
+
+ Call MKTARENT
+
+ 'CALLPIPE VAR TARENT | E2A | *:'
+ 'CALLPIPE <' fn ft fm pipe '| FBLOCK 512 00 | *:'
+ If verbose Then Say "a" name || "," size "bytes," trans lrecl
+
+ End /* Do Forever */
+
+/* a trailer of nulls */
+'OUTPUT' Copies('00'x,512)
+
+Return
+
+
+/* ---------------------------------------------------------------------
+ * extract
+ */
+XTRACT:
+
+'ADDPIPE *: | FBLOCK 512 | *.INPUT:'
+'CALLPIPE *: | TAKE' skip '| HOLE'
+
+Parse Var args xtf xfn xft xfm .
+If xfn = "" Then xfn = "="
+If xft = "" Then xft = "="
+If xfm = "" Then xfm = "A"
+
+Do Forever
+
+ 'PEEKTO'
+ If rc ^= 0 Then Leave
+
+ 'CALLPIPE *: | TAKE 1 | A2E | VAR RECORD'
+ Call EXTARENT
+ If size = 0 & name = "" Then Leave
+ If size = 0 Then Iterate
+ If name ^= xtf & xtf ^= '*' & xtf ^= '' Then Do
+ 'CALLPIPE *: | TARTAKE' size '| HOLE'
+ Iterate
+ End /* If .. Do */
+
+ Parse Value Reverse(name) With basename '/' .
+ basename = Reverse(basename)
+ Parse Upper Var basename fn '.' ft '.' .
+ If fn = "" Then fn = Userid()
+ If xfn ^= "=" Then fn = xfn
+ If ft = "" Then ft = "$"
+ If xft ^= "=" Then ft = xft
+ fm = xfm
+ filespec = fn ft fm
+
+ If ^peek Then Do
+ 'CALLPIPE STATE' filespec
+ If rc = 0 Then Do
+ If ^replace Then Do
+ If verbose Then ,
+ Say "x" name || "," size "bytes," ,
+ (size+511)%512 "tape blocks"
+ Address "COMMAND" 'XMITMSG 24 FILESPEC (CALLER TAR'
+ Leave
+ End /* If .. Do */
+ Else Address "COMMAND" 'ERASE' filespec
+ End /* If .. Do */
+ End /* If .. Do */
+
+ If trans ^= 'T' & trans ^= 'B' Then Do
+ 'PEEKTO RECORD'
+ If size < 512 Then record = Left(record,size)
+ If Verify(record,a_nprint,'M') = 0 Then Do
+ trans = 'T'
+ lrecl = 0
+ End /* If .. Do */
+ Else Do
+ trans = 'B'
+ If size < 65536 Then lrecl = size
+ Else lrecl = 512
+ End /* Else Do */
+ End /* If .. Do */
+
+ Select
+ When trans = 'T' & lrecl = 0 Then
+ pipe = 'DEBLOCK LINEND 0A | STRIP TRAILING 0D' ,
+ '| DROP LAST | A2E | PAD 1'
+ When trans = 'T' & lrecl > 0 Then ,
+ pipe = 'DEBLOCK LINEND 0A | STRIP TRAILING 0D' ,
+ '| DROP LAST | A2E | PAD' lrecl
+ When trans = 'B' & lrecl = 0 Then ,
+ pipe = 'DEBLOCK CMS'
+ When trans = 'B' & lrecl > 0 Then ,
+ pipe = 'FBLOCK' lrecl '00'
+ When trans = 'V' Then ,
+ pipe = 'DEBLOCK CMS'
+ End /* Select */
+
+ If lrecl = 0 Then fix = ""
+ Else fix = "FIXED" lrecl
+
+ If verbose Then ,
+ Say "x" name || "," size "bytes," ,
+ (size+511)%512 "tape blocks, as" ,
+ filespec || fmode trans fix
+ 'CALLPIPE' ,
+ '*: | TARTAKE' size '|' pipe '| > TAR CMSUT1' fm || '3' fix
+
+ If peek Then Do
+ Address "COMMAND" 'MAKEBUF'
+ Push "COMMAND MSG x" name || "," size "bytes," ,
+ (size+511)%512 "tape blocks"
+ Push "COMMAND SET FN" fn
+ Push "COMMAND SET FT" ft
+ Push "COMMAND SET FM" fm || fmode
+ Address "COMMAND" 'XEDIT TAR CMSUT1' fm
+ Address "COMMAND" 'DROPBUF'
+ End /* If .. Do */
+ Else Do
+ Address "COMMAND" 'RENAME TAR CMSUT1' fm filespec || fmode
+ Address "COMMAND" 'DMSPLU' filespec date time
+ End /* Else Do */
+
+ If once Then Leave
+ If xtf ^= '*' & xtf ^= '' Then Leave
+
+ End /* Do Forever */
+
+Return
+
+
+/* ---------------------------------------------------------------------
+ * list table of contents
+ */
+LISTOC:
+
+'ADDPIPE *: | FBLOCK 512 | *.INPUT:'
+'CALLPIPE *: | TAKE' skip '| HOLE'
+
+Do Forever
+
+ 'PEEKTO'
+ If rc ^= 0 Then Leave
+
+ 'CALLPIPE *: | TAKE 1 | A2E | VAR RECORD'
+ Call EXTARENT
+ If size = 0 & name = "" Then Leave
+
+ If size > 0 Then Do
+ Select
+ When tarlist Then 'OUTPUT' " " || Left(name,46) ,
+ Right(size,8) Right(date,8) Right(time,8) ,
+ Right(skip,8) name
+/* trans recfm lrecl fmode */
+ When verbose Then 'OUTPUT' Left(name,42) '-' ,
+ Right(date,8) Right(time,8) Right(size,8) "bytes."
+ Otherwise 'OUTPUT' name
+ End /* Select */
+
+ take = Trunc((size + 511) / 512)
+ 'CALLPIPE *: | TAKE' take '| HOLE'
+ skip = skip + take
+ End /* If .. Do */
+ skip = skip + 1
+
+ If args ^= "" Then Leave
+
+ End /* Do Forever */
+
+Return
+
+
+/* ------------------------------------------------------------ EXTARENT
+ * Extract TAR entry (directory info) values.
+ * Sets: size, and other variables.
+ */
+EXTARENT:
+Parse Var record 1 name '00'x .
+record = Translate(record,' ','00'x)
+Parse Upper Var record 101 perm . ,
+ 125 size date chksum trans lrecl fmode . ,
+ 257 . ,
+ 385 .
+size = o2d(size) /* convert to decimal */
+If size > 0 Then Do
+ Parse Value sysdate(o2d(date)) With date time .
+ chksum = o2d(chksum) /* convert to decimal */
+ lrecl = o2d(lrecl) /* convert to decimal */
+ If lrecl = 0 Then recfm = 'V'
+ Else recfm = 'F'
+ If ^Datatype(fmode,'N') Then fmode = '1'
+ End /* If .. Do */
+
+Return
+
+
+/* ------------------------------------------------------------------ */
+O2D: Procedure /* Octal to Decimal conversion */
+Parse Arg o
+d = 0
+Do While o ^= ""
+ Parse Var o 1 c 2 o
+ If Datatype(c,'N') Then d = d * 8 + c
+ End /* Do While */
+Return d
+
+
+/* ------------------------------------------------------------------ */
+D2O: Procedure /* Decimal to Octal conversion */
+Parse Arg d
+If ^Datatype(d,'N') Then d = 0
+d = trunc(d)
+If d < 1 Then Return 0
+o = ""
+Do While d ^= 0
+ o = d // 8 || o
+ d = d % 8
+ End /* Do While */
+Return o
+
+
+/* ------------------------------------------------------------------ */
+DATEREAD: Procedure /* return textual date from octal */
+Parse Arg date
+
+year = 1970
+Do Forever
+ If year - (year % 4) * 4 = 0 Then days = 366
+ Else days = 365
+ If date < days Then Leave
+ date = date - days
+ year = year + 1
+ End
+
+Return year || '.' || date
+
+
+/* ------------------------------------------------------------------ */
+DATEMAKE: Procedure /* return octal date from textual */
+Parse Arg date
+
+_m.1 = 31; _m.2 = 28; _m.3 = 31; _m.4 = 30
+_m.5 = 31; _m.6 = 30; _m.7 = 31; _m.8 = 31
+_m.9 = 30; _m.10 = 31; _m.11 = 30; _m.12 = 31
+
+year = 1970
+Do Forever
+ If year - (year % 4) * 4 = 0 Then days = 366
+ Else days = 365
+ If date < days Then Leave
+ date = date - days
+ year = year + 1
+ End
+
+Return year || '.' || date
+
+/*
+If yy // 4 = 0 & yy // 100 ^= 0 Then _m.2 = 29
+ Else _m.2 = 28
+ */
+
+
+/* ------------------------------------------------------------- TARDATE
+ */
+TARDATE: Procedure Expose dfmt tzos
+Parse Arg date time .
+Parse Var time hh ':' mm ':' ss
+Parse Var date mo '/' dd '/' yy /* If dfmt = 'U' */
+ly = (yy // 4 = 0 & (yy // 100 ^= 0 | yy // 400 = 0))
+Select /* mo */
+ When mo = 1 Then nop
+ When mo = 2 Then dd = dd + 31
+ When mo = 3 Then dd = dd + 59 + ly
+ When mo = 4 Then dd = dd + 90 + ly
+ When mo = 5 Then dd = dd + 120 + ly
+ When mo = 6 Then dd = dd + 151 + ly
+ When mo = 7 Then dd = dd + 181 + ly
+ When mo = 8 Then dd = dd + 212 + ly
+ When mo = 9 Then dd = dd + 243 + ly
+ When mo = 10 Then dd = dd + 273 + ly
+ When mo = 11 Then dd = dd + 304 + ly
+ When mo = 12 Then dd = dd + 334 + ly
+ End /* Select mm */
+Do yy = yy - 1 to 70 by -1
+ ly = (yy // 4 = 0 & (yy // 100 ^= 0 | yy // 400 = 0))
+ dd = dd + 365 + ly
+ End /* Do For */
+dd = dd - 1
+Return dd*86400+hh*3600+mm*60+ss-tzos
+
+
+/* ------------------------------------------------------------- SYSDATE
+ */
+SYSDATE: Procedure Expose dfmt tzos
+Parse Arg base .
+base = base + tzos
+dd = base % 86400 + 1
+time = base // 86400
+yy = 70
+ly = (yy // 4 = 0 & (yy // 100 ^= 0 | yy // 400 = 0))
+Do While dd > 365 + ly
+ yy = yy + 1
+ dd = dd - 365 - ly
+ ly = (yy // 4 = 0 & (yy // 100 ^= 0 | yy // 400 = 0))
+ End /* Do While */
+Select /* mo */
+ When dd <= 31 Then mm = 1
+ When dd <= 59 + ly Then Do; mm = 2; dd = dd - 31; End
+ When dd <= 90 + ly Then Do; mm = 3; dd = dd - 59 - ly; End
+ When dd <= 120 + ly Then Do; mm = 4; dd = dd - 90 - ly; End
+ When dd <= 151 + ly Then Do; mm = 5; dd = dd - 120 - ly; End
+ When dd <= 181 + ly Then Do; mm = 6; dd = dd - 151 - ly; End
+ When dd <= 212 + ly Then Do; mm = 7; dd = dd - 181 - ly; End
+ When dd <= 243 + ly Then Do; mm = 8; dd = dd - 212 - ly; End
+ When dd <= 273 + ly Then Do; mm = 9; dd = dd - 243 - ly; End
+ When dd <= 304 + ly Then Do; mm = 10; dd = dd - 273 - ly; End
+ When dd <= 334 + ly Then Do; mm = 11; dd = dd - 304 - ly; End
+ Otherwise Do; mm = 12; dd = dd - 334 - ly; End
+ End /* Select dd */
+Return Right(mm,2,'0') || '/' || ,
+ Right(dd,2,'0') || '/' || ,
+ Right(yy,2,'0') ,
+ Right(time%3600,2,'0') || ':' || ,
+ Right((time//3600)%60,2,'0') || ':' || ,
+ Right(time//60,2,'0')
+
+
+/* ------------------------------------------------------------ MKTARENT
+ * Create a TAR entry (directory info) from values.
+ */
+MKTARENT:
+
+chksum = 0
+
+tarent = Left(name,100,'00'x) || ,
+ Right(perm,6) '00'x || ,
+ " 1" '00'x || " 1" '00'x || ,
+ Right(d2o(size),11) ,
+ Right(d2o(tardate(date time)),11) ,
+ Right(d2o(chksum),6) || '00'x || ,
+ Right(trans,2) ,
+ Right(d2o(lrecl),8) ,
+ Right(fmode,1) || '00'x
+
+'CALLPIPE VAR TARENT | E2A | VAR TARENT'
+Do i = 1 to Length(tarent)
+ chksum = chksum + c2d(Substr(tarent,i,1))
+ End
+
+chksum = chksum + 16
+
+
+tarent = Left(name,100,'00'x) || ,
+ Right(perm,6) '00'x || ,
+ " 1" '00'x || " 1" '00'x || ,
+ Right(d2o(size),11) ,
+ Right(d2o(tardate(date time)),11) ,
+ Right(d2o(chksum),6) || '00'x || ,
+ Right(trans,2) ,
+ Right(d2o(lrecl),8) ,
+ Right(fmode,1) || '00'x
+
+tarent = Left(tarent,256,'00'x)
+tarent = Left(tarent,512,'00'x)
+
+Return
+
+tarent02 = '00'x || "ustar" || '00'x || "00" || ,
+ Left(userid,32,'00'x) || Left(groupid,32,'00'x) || ,
+ "0000000" || '00'x || "0000000" || '00'x
+
+Return
+
+
+/* ------------------------------------------------------------ TZOFFSET
+ * Compute timezone offset based on timezone string from 'CP Q TIME'.
+ * (we probably have a CSL routine to do this ... but maybe not)
+ */
+TZOFFSET: Procedure
+
+Parse Upper Arg denom . ',' . , .
+Parse Upper Value Diag(08,'QUERY TIME') With . . . tz .
+
+Select /* tz */
+ When tz = "CST" Then zo = -6
+ When tz = "CDT" Then zo = -5
+ When tz = "EST" Then zo = -5
+ When tz = "EDT" Then zo = -4
+ Otherwise zo = 0
+ End /* Select tz */
+
+denom = Left(denom,1)
+Select /* denom */
+ When denom = "S" Then Return zo * 60 * 60 /* offset in seconds */
+ When denom = "M" Then Return zo * 60 /* offset in minutes */
+ Otherwise Return zo
+ End /* Select denom */
+
+
diff --git a/vmworkshop-vmarcs/1996/tar/tarindex.rexx b/vmworkshop-vmarcs/1996/tar/tarindex.rexx
new file mode 100644
index 0000000..8a2bc27
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/tar/tarindex.rexx
@@ -0,0 +1,70 @@
+/*
+ * Name: TARINDEX REXX
+ * create a CMS TAR "include file" (FILELIST format)
+ * Author: Rick Troth, Houston, Texas, USA
+ * Date: 1992-Oct-01, 1993-Feb-01
+ */
+
+Parse Upper Arg fn ft fm fp . '(' .
+Select
+ When fp ^= "" Then nop
+ When fn = '.' Then Do
+ fn = '*'; ft = '*'
+ fm = 'A'; fp = '.'
+ End
+ When fm = '.' Then Do
+ fm = 'A'; fp = '.'
+ End
+ Otherwise Do
+ If fn = "" Then fn = '*'
+ If ft = "" Then ft = '*'
+ If fm = "" Then fm = 'A'
+ 'CALLPIPE COMMAND QUERY DISK' fm '| DROP' ,
+ '| SPEC 1.6 1 | STRIP | VAR FP'
+ If rc ^= 0 Then Exit rc
+ If fp = '-' Then fp = '.'
+ End /* Otherwise Do */
+ End /* Select */
+
+'ADDPIPE COMMAND LISTFILE' fn ft fm '(ALLOC ALLFILE NOHEADER | *.INPUT:'
+If rc ^= 0 & rc ^= 28 Then ,
+ 'ADDPIPE COMMAND LISTFILE' fn ft fm '| *.INPUT:'
+If rc ^= 0 & rc ^= 28 Then Exit rc
+If rc = 28 Then Exit 0
+
+Parse Source . . arg0 .
+
+uc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+lc = "abcdefghijklmnopqrstuvwxyz"
+
+Do Forever
+
+ 'PEEKTO FILEID'
+ If rc ^= 0 Then Leave
+
+ Parse Var fileid _fn _ft _fm .
+
+ If _fm = "DIR" Then Do
+ 'READTO'
+ If rc ^= 0 Then Leave
+ 'CALLPIPE COMMAND ACCESS +' || _ft || '.' || _fn _ft
+ If rc ^= 0 Then Iterate
+ _fp = Translate(fp || '/' || _fn, lc, uc)
+ 'OUTPUT' "*!mkdir" _fp
+ 'CALLPIPE' arg0 '* *' _ft _fp '| *:'
+ 'CALLPIPE COMMAND ACCESS -' || _ft _ft
+ Iterate
+ End /* If .. Do */
+
+ 'OUTPUT' " " _fn _ft _fm ,
+ Translate(fp || '/' || _fn || '.' || _ft, lc, uc)
+ If rc ^= 0 Then Leave
+
+ 'READTO'
+ If rc ^= 0 Then Leave
+
+ End /* Do While */
+
+Exit rc * (rc ^= 12)
+
+
diff --git a/vmworkshop-vmarcs/1996/tar/tarlist.exec b/vmworkshop-vmarcs/1996/tar/tarlist.exec
new file mode 100644
index 0000000..7dca4be
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/tar/tarlist.exec
@@ -0,0 +1,12 @@
+/*
+ * Name: TARLIST EXEC
+ * rather like FILELIST, but for TAR archives
+ * Copyright 1992, Richard M. Troth
+ */
+
+Parse Arg fn ft fm args '(' opts ')' rest
+If fn = "" Then fn = "TAP1"
+'XEDIT' fn 'TARLIST A0 (WIDTH 240 PROFILE PROFTLST)' ,
+ fn ft fm args '(' opts ')' rest
+Exit rc
+
diff --git a/vmworkshop-vmarcs/1996/tar/tarlist.helpcms b/vmworkshop-vmarcs/1996/tar/tarlist.helpcms
new file mode 100644
index 0000000..4093d9e
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/tar/tarlist.helpcms
@@ -0,0 +1,27 @@
+.cm This file was created by hand.
+.cm
+.cs 1 on
+
+¢|TARLIST EXEC¢%
+
+ Use the TARLIST command to browse a TAR file similar to FILELIST.
+
+.cs 1 off
+.cs 2 on
+ The format of the TARLIST command is:
+ +------------+-------------------------------------------------------------+
+ | | |
+ | TARLIST | tar-file |
+ | | |
+ +------------+-------------------------------------------------------------+
+
+ Note: TARLIST is *not* well developed. The task of writing
+ a utility from scratch that functions like FILELIST
+ has been overwhelming. PF9 and PF11 work as listed,
+ but don't expect anything else.
+
+.cs 2 off
+.cs 3 on
+.cs 3 off
+.cs 4 on
+.cs 4 off
diff --git a/vmworkshop-vmarcs/1996/tar/tarpunch.rexx b/vmworkshop-vmarcs/1996/tar/tarpunch.rexx
new file mode 100644
index 0000000..54c15f0
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/tar/tarpunch.rexx
@@ -0,0 +1,50 @@
+/* © Copyright 1995, Richard M. Troth, all rights reserved.
+ *
+ * Name: TARPUNCH REXX
+ * "punch" a tar "deck" to another user
+ * Copyright 1992, 1995, Richard M. Troth
+ */
+
+Parse Arg target . '(' . ')' .
+
+/* if target is a dash, feed to punch w/o closing */
+If target = '-' Then Do
+ 'CALLPIPE *.INPUT: | FBLOCK 80 00 | PUNCH'
+ Exit rc
+ End /* If .. Do */
+
+/* okay, so we really want to send this */
+'CALLPIPE COMMAND IDENTIFY | VAR IDENTITY'
+Parse Var identity userid . hostid . rscsid . '15'x .
+Parse Var target user '@' host
+If user = "" Then user = userid
+If host = "" Then host = hostid
+
+/* careful! this is a raw SIFT/UFT job */
+Address "COMMAND" 'STATE UFTCHOST REXX *'
+If rc = 0 Then Do
+ 'ADDPIPE *.OUTPUT: | UFTCHOST' host '(TYPE I | *.OUTPUT:'
+ 'CALLPIPE VAR USERID | XLATE LOWER | VAR USERID'
+ 'OUTPUT FILE 0' userid '-'
+/* 'OUTPUT USER' user || '@' || host */
+ 'OUTPUT USER' user
+ 'OUTPUT TYPE I'
+ 'OUTPUT DATA'
+/* 'SHORT' */
+ 'CALLPIPE *: | *:'
+ End /* If .. Do */
+
+/* that didn't work, so punt to RSCS */
+Else Do
+ Address "COMMAND" 'GETFMADR'
+ If rc ^= 0 Then Exit rc
+ Parse Pull . . tmp .
+ 'CALLPIPE CP DEFINE PUNCH' tmp
+ 'CALLPIPE CP TAG DEV' tmp host user '50'
+ 'CALLPIPE CP SPOOL' tmp 'TO' rscsid
+ 'CALLPIPE *.INPUT: | FBLOCK 80 00 | SPEC x41 1 1-* NEXT | URO' tmp
+ 'CALLPIPE CP DETACH' tmp
+ End /* Else Do */
+
+Exit
+
diff --git a/vmworkshop-vmarcs/1996/tar/tarreadc.rexx b/vmworkshop-vmarcs/1996/tar/tarreadc.rexx
new file mode 100644
index 0000000..5668981
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/tar/tarreadc.rexx
@@ -0,0 +1,35 @@
+/*
+ * Name: TARREADC REXX
+ * read a tar "deck" in the reader
+ * Copyright 1992, Richard M. Troth
+ */
+
+rdr = "00C"
+
+Parse Arg tf . '(' . ')' .
+
+'CALLPIPE CP QUERY VIRTUAL' rdr '| VAR READER'
+Parse Var reader . . . . . hold . '15'x .
+'CALLPIPE CP SPOOL' rdr 'HOLD'
+'CALLPIPE CP CLOSE' rdr
+
+If tf ^= "-" Then Do
+ 'CALLPIPE CP ORDER READER' tf '| VAR RS'
+ If rc ^= 0 Then Do
+ orc = rc
+ 'OUTPUT' rs
+ 'CALLPIPE CP SPOOL' rdr hold
+ Exit orc
+ End /* If .. Do */
+ End /* If .. Do */
+
+'CALLPIPE READER' rdr ,
+ '| NLOCATE 1-1 /' || '03'x || '/' ,
+ '| SPEC 2-* 1 | PAD 80 40 | *:'
+
+'CALLPIPE CP CLOSE' rdr
+'CALLPIPE CP SPOOL' rdr hold
+
+Exit
+
+
diff --git a/vmworkshop-vmarcs/1996/tar/tartake.rexx b/vmworkshop-vmarcs/1996/tar/tartake.rexx
new file mode 100644
index 0000000..55f1742
--- /dev/null
+++ b/vmworkshop-vmarcs/1996/tar/tartake.rexx
@@ -0,0 +1,25 @@
+/*
+ * Name: TARTAKE REXX
+ * pass an exact number of bytes
+ * while consuming an integral number of 512-byte records.
+ * Copyright 1993, Richard M. Troth
+ */
+
+Parse Arg size . '(' . ')' .
+If ^Datatype(size,'N') Then Exit
+If size = 0 Then Exit
+
+full = size % 512
+'CALLPIPE *: | TAKE' full '| *:'
+If rc ^= 0 Then Exit rc
+
+part = size // 512
+If part = 0 Then Exit
+'PEEKTO RECORD'
+'OUTPUT' Left(record,part)
+'READTO'
+If rc ^= 0 Then Exit rc
+
+Exit
+
+
diff --git a/vmworkshop-vmarcs/2012/README.md b/vmworkshop-vmarcs/2012/README.md
new file mode 100644
index 0000000..be53562
--- /dev/null
+++ b/vmworkshop-vmarcs/2012/README.md
@@ -0,0 +1,3 @@
+## Source
+
+Code from VMARCs distributed as part of the 2012 VM Workshop.
diff --git a/vmworkshop-vmarcs/2012/qlabs/qlabs.exec b/vmworkshop-vmarcs/2012/qlabs/qlabs.exec
new file mode 100644
index 0000000..d5fffa6
--- /dev/null
+++ b/vmworkshop-vmarcs/2012/qlabs/qlabs.exec
@@ -0,0 +1,105 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/* This exec is intended to list VOL1 and CMS1 label enteries from a */
+/* formatted disk. */
+/* */
+/* One parameter is required and may be entered either */
+/* at invocation or in response to a prompt. */
+/* It is: 1) unit address */
+/* An option is available to specify a range of cylinders to scan */
+/* .. useful to limit the scope of the label search */
+/* */
+/* FORMAT: QLABS vdev (startcyl endcyl */
+/* DEFAULTS: startcyl = 0 */
+/* endcyl = startcyl */
+/* NOTE: endcyl may be entered as * or END to scan to the end of the */
+/* volume */
+/* */
+/* *** for these examples assume MAINT 123 linked as 123 *** */
+/* EXAMPLE: QLABS 123 see the volume label for 123 */
+/* EXAMPLE: QLABS 123 (39 should show MNTCF1 (z/VM 6.2) */
+/* EXAMPLE: QLABS 123 (39 1000 show all labels on 123 between */
+/* cylinders 39 and 1000 */
+/* EXAMPLE: QLABS 123 (0 END show all labels on 123 between */
+/* cylinders 0 thru the last cylinder */
+/* */
+/* Dependencies and pre-reqs: */
+/* */
+/* The pipe stage 'trackread' is used which is available in the */
+/* Princeton University pipelines package. */
+/* http://vm.marist.edu/~pipeline/index.html#Runtime */
+/* */
+/* PICKPIPE which can be downloaded from IBM's VM download page. */
+/* http://www.vm.ibm.com/download/packages/ */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+ parse source . . exname . /* get the EXEC name for displays */
+ arg unit . '(' start end . /* parse out the args */
+ if unit = '' then do /* do we have the unit addr? */
+ say 'CUU -- START & END cylinders' /* no.. ask for it */
+ pull unit start end . /* get the response */
+ end
+ if start = '' then start = 0 /* if not specified start on cyl 0 */
+ if end = '' then end = start /* if not specified end on the start cyl */
+ rrc = 0 /* initialize */
+ ncms = 0 /* initialize */
+ ycms = 0 /* initialize */
+ 'pipe query version | spec 1-3 1 | var pipelevel'
+ if pipelevel ¬= 'PIP' then, /* do we have correct pipes?*/
+ pickpipe u '(quiet' /* no then load the UPLEVEL pipes */
+ if rc ¬= 0 then /* was pipe load successful? */
+ call msg 16, 'An uplevel pipe package is not available-- 'exname ' cannot continue'
+
+ 'pipe devinfo' unit '| var temp' /* get device characteristics */
+ if rc ¬= 0 then /* device error */
+ call msg 20, 'Device error trying to access ' unit '--' exname 'cannot continue'
+ parse var temp cuu clas dvtyp cutyp cyls trkpc trklen . /* parse it out to vars */
+
+ if dvtyp ¬= '3390' then /* device type must be 3390 */
+ call msg 24, 'Device' cuu 'is not a supported device type' dvtyp '¬= 3390'
+
+ if end = '*' | end = 'END' then end = cyls -1 /* calculate the end cylinder */
+
+ if end < start then /* verify cylinder range */
+ call msg 26, 'END less than START on' unit ' --' exname 'cannot continue'
+
+ if end > cyls -1 then /* verify cylinder range */
+ call msg 28, 'END ('end') > last cylinder ('cyls -1') on' unit ' --' exname 'cannot continue'
+
+ sscan = start /* start scan cylinder */
+ escan = end /* end scan cylinder */
+ do start = start by 1 until start = end /* loop read specified cylinders */
+ 'PIPE (endchar ? )', /* start the pipe */
+ '| trackread' unit start '0 ', /* read a track */
+ '| trackdeblock ', /* deblock the track */
+ '| drop first 4', /* ... possible label */
+ '| specs 09-28 1 ', /* extract the label TYPE & NAME */
+ '| var r3vol ' /* save TYPE and NAME for later */
+ if rc ¬= 0 then /* device error? */
+ call msg 30, 'Device error see previous messages --' exname 'cannot continue'
+
+ select;
+ when substr(r3vol,1,4) = 'VOL1' then do /* is this a non-CMS formatted extent */
+ call msg 00, 'VOLUME label for' unit 'at cylinder' start 'is' substr(r3vol,9,6)
+ ncms = ncms +1 /* count the non-CMS labels */
+ end
+ when substr(r3vol,1,4) = 'CMS1' then do /* is this a CMS formatted extent? */
+ call msg 00, 'CMS label for' unit 'at cylinder 'start 'is ' substr(r3vol,5,6)
+ ycms = ycms +1 /* count the CMS labels */
+ end
+ otherwise;
+ end /* return and read the next track */
+ end
+ call msg 00, exname 'scanned cylinders' sscan 'through' escan
+ call msg 00, exname 'Finished normally and detected' ncms 'non-CMS labels and 'ycms' CMS labels'
+signal finish
+
+msg: /* messages */
+ parse arg rrc, txt /* parse the message into its parts */
+ say txt /* display the message */
+ if rrc = 0 then return
+ signal finish /* wrap it up and exit */
+
+finish:
+ if pipelevel ¬= 'PIP' then, /* did we start with modern pipes? */
+ pickpipe cms '(quiet' /* no .. put back CMS pipes */
+exit rrc /* exit */
diff --git a/vmworkshop-vmarcs/2012/splcksum/splcksum.exec b/vmworkshop-vmarcs/2012/splcksum/splcksum.exec
new file mode 100644
index 0000000..563b8b1
--- /dev/null
+++ b/vmworkshop-vmarcs/2012/splcksum/splcksum.exec
@@ -0,0 +1,52 @@
+/*----------------------------------------*/
+/* Display the checksum on an NSS or DCSS */
+/* */
+/* Syntax: */
+/* SPLCKSUM */
+/* */
+/* */
+/* */
+/* */
+/*----------------------------------------*/
+
+parse upper arg infile rest
+If rest <> '' then
+ Do
+ say 'ERROR: Extra parameter was specified -- 'rest
+ exit 4
+ End
+
+'pipe CP QUERY PRIVCLAS ',
+' | drop first 1 ',
+' | drop last 2 ',
+' | spec w2 1 ',
+' | var pclass '
+Do l = 1 to length(pclass)
+ If substr(pclass,l,1) = 'E' then leave
+ If l = length(pclass) then
+ Do
+ say 'ERROR: Privilege class E is required.'
+ say ' You have only the following classes -- 'pclass
+ exit 8
+ End
+End
+
+'pipe CP Q NSS | spec w2 1 w8 10 | stem outrec.'
+
+Do i = 2 to outrec.0
+ spid = word(outrec.i,1)
+ name = word(outrec.i,2)
+ If name = infile then leave
+ If i = outrec.0 then
+ Do
+ say 'ERROR: Requested NSS or DCSS was not found -- 'infile
+ exit 12
+ End
+End
+
+'pipe CP LOCATE SPFBK *NSS 'spid ' | drop first 1 | spec w4 | var spf@'
+spfsha1s@ = d2x(x2d(spf@) + 112)
+'pipe CP DISPLAY HL' || spfsha1s@ || '.20 ',
+' | join 1 | spec w2 1 w3 10 w4 19 w5 28 w8 37 | var cksm'
+say infile ' checksum: 'cksm
+exit 0