Overview Overview Overview Overview RSTS/E lends itself well to customization for a particular site's requirements. Frequently these customizations can be performed using DCL command procedures or simple Basic-Plus programs. However, some customizations require changing the way RSTS handles user commands. Such customizations cannot be made using only DCL and Basic-Plus programs. This presentation will cover some sample customization cases and present tools which can be used in your own customization work. Please remember that DEC can only support unmodified unmodified unmodified versions of the software. If you experience a problem with a utility after you've changed it, please verify the problem exists in an unmodified version before reporting it to DEC. Additionally, many of the examples given depend on the (undocumented) internal formats of system items. While all have been tested with the latest release of RSTS/E, they may fail to function properly with either older or newer releases. A liberal amount of time has been allotted for questions and answers from the audience. In addition, questions may be directed to the RSTS SIG Newsletter System at (201) 915-9361. In addition, the sources and executables for all examples given are available on-line for downloading. Example 1 - Changing the effect of a system command Example 1 - Changing the effect of a system command Example 1 - Changing the effect of a system command Example 1 - Changing the effect of a system command Requirement: Have the SHOW NETWORK command display the status of all known nodes, rather than just the adjacent nodes. The 'tool' we will use to investigate the system's processing of the SHOW NETWORK command is part of DCL itself - the SET VERIFY command. This command has an undocumented function which will prove helpful, but first let's look at one of the documented functions: o It can show you the command line after symbol substitution: $ DISPLAY == "SHOW" ! define display as a synonym for show $ SET VERIFY/DEBUG ! turn on debugging display $ DISPLAY NETWORK ! issue command (SHOW NETWORK) ! displays substituted command ... output ... o It can also show you the actual command that DCL issues to perform the task: 1 $ SET VERIFY/WATCH ! turn on translation display $ SHOW NETWORK ! issue command (NCP SHO ACT NOD) ! displays actual command issued ... output ... These two options can be very useful when you have a command procedure which isn't acting as planned. The /DEBUG option will rapidly show you if you are experiencing undesired symbol substitution. The /WATCH option will show you exactly what command is being executed - in the example above, it explains why DECNET/E only shows active nodes rather than all nodes, which would be NCP SHO KNO NOD. That's the problem - we're doing a show active instead of a show known command. What we wanted was NCP SHO KNO NOD. This brings out an important point - strings we replace must be the SAME LENGTH as the originals. Now, we'll copy the existing DCL.RTS to a temp file for our changes: $ COPY [0,1]DCL.RTS [0,1]NDCL.RTS Next, we'll invoke the SCAN program (listing at end of handout or on the newsletter system as DU0:[49,1]SCAN.BAS) to locate the text string. $ RUN SCAN Binary file search 1.20 - 17-Nov-87 KS File? [0,1]ndcl.rts Search string? ACT NOD Found in block 97 (000140), offset 205 (000314). Done... $ We can compute the address used by ODT as follows: Disk block (in octal): 000140 + Offset (in octal): 000314 --------- 000140314 (or 140314 for short) Now that we know the ODT address, we will need to know the octal values for the replacement text (ACT versus KNO). We can get this by doing HELP ADVANCED ASCII. From the chart that is displayed, we see that K is 113 octal, N is 116, and O is 117. Now we will run the ODT program to actually modify the text in our copy of the DCL run-time system. The text in BOLD ITALICS BOLD ITALICS BOLD ITALICS is what you type. $ run auxlib$:odt run auxlib$:odt run auxlib$:odt ODT V9.4-01 RSTS V9.4-05 Newsletter Sys. File? [0,1]ndcl.rts [0,1]ndcl.rts [0,1]ndcl.rts 2 [display the text to make sure we are at the right spot] *140314/ 041501 " AC 140314/ " 140314/ " 140316/ 020124 " T " " 140320/ 047516 " NO " " 140322/ 000104 " D " " [look at the previous 4 bytes to make sure] *140310/ 044123 " SH 140310/ " 140310/ " 140312/ 020117 " O " " [now we will modify the ACT to KNO using the octal codes] *140314\ 101 113 140314\ 113 140314\ 113 140315\ 103 116 116 116 140316\ 124 117 117 117 [now let's make sure the changes were correct] *140314/ 047113 " KN 140314/ " 140314/ " 140316/ 020117 " O " " [all done, let's exit] *^Z ^Z ^Z Now, make SURE that you have only changed the bytes that you wanted to change. ANY extraneous changes will cause SEVERE problems sooner or later. In the worst case, you might not even be able to reboot your system! If you are ABSOLUTELY SURE, issue the following command to replace the old DCL run- time system: $ copy [0,1]ndcl.rts [0,1]dcl.rts OK to replace existing file DU0:[0,1]DCL .RTS ? y [File [0,1]NDCL .RTS copied to [0,1]DCL .RTS] Now, reboot your system in order for the change to take effect. You can verify this after rebooting by re-issuing the original set of commands, as follows: $ SET VERIFY/WATCH $ SHOW NETWORK (NCP SHO KNO NOD) If you have made an error, restore DCL.RTS by re-booting your RSTS installation tape (you DO know where it is, right?) and specifying an UPDATE (as opposed to an installation). Specify your system disk name and let it proceed. Answer 'NO' to 'Start timesharing?'. At the 'Option?' prompt, say INSTALL monitorname, where monitorname is the name of your system monitor (usually RSTS). Now you may re-start your system as usual. 3 Example 2 - Changing the effect of a system command Example 2 - Changing the effect of a system command Example 2 - Changing the effect of a system command Example 2 - Changing the effect of a system command Requirement: Enhance Print/Batch Services (PBS) so that a user is notified of batch job completion. The way to do this involves modifying the PBS server task, similarly to the way we modified the DCL tables above. We want to replace the string '$_LOGOUT/BRIEF' in PBS$:PBS.TSK with another command. Probably the best thing to do would be to invoke a .COM file. Oddly enough, the string '$_@$BATFIN.COM' is exactly the same length, so that is what we will replace it with. Let's use the SCAN program to find the correct place: $ run scan Binary file search 1.20 - 17-Nov-87 KS File? pbs$:pbs.tsk Search string? $_LOGOU Found in block 99 (000142), offset 504 (000767). Done... We can compute the address used by ODT as follows: Disk block (in octal): 000142 + Offset (in octal): 000767 --------- 000142767 (or 142767 for short) Now that we know the ODT address, we will need to know the octal values for the replacement text. As we did above, we can determine these values from the chart in HELP ADVANCED ASCII. Now we will run the ODT program to actually modify the text in our copy of the PBS task.. The text in BOLD ITALICS is what BOLD ITALICS BOLD ITALICS you type. $ run auxlib$:odt run auxlib$:odt run auxlib$:odt ODT V9.4-01 RSTS V9.4-05 Newsletter Sys. File? pbs$:pbs.tsk pbs$:pbs.tsk pbs$:pbs.tsk *142767/ 044 " $ 142767/ " 142767/ " 142770\ 137 " _ " " 142771\ 114 " L " " [look at the data to make sure we're in the right spot] *142771\ 114 100 142771\ 100 142771\ 100 142772\ 117 044 044 044 142773\ 107 102 102 102 142774\ 117 101 101 101 142775\ 125 124 124 124 142776\ 124 106 106 106 142777\ 057 111 111 111 143000\ 102 116 116 116 143001\ 122 056 056 056 143002\ 111 103 103 103 4 143003\ 105 117 117 117 143004\ 106 115 115 115 143005\ 015 [enter the new text] *142770/ 040137 " _@ 142770/ " 142770/ " 142772/ 041044 " $B " " 142774/ 052101 " AT " " 142776/ 044506 " FI " " 143000/ 027116 " N. " " 143002/ 047503 " CO " " 143004/ 006515 " M " " [verify changes] *^Z Now, make SURE that you have only changed the bytes that you wanted to change. ANY extraneous changes will cause SEVERE problems sooner or later. If you have any problems with PBS because of this change, simply restore PBS$:PBS.TSK from your most recent backup (you do have one, don't you?). Next, we will need to create the .COM file in the $ account. Remember that this file will be executed by users who may not have requisite privileges for some operations. However, with some deviousness, you can work around this. You could also write a program privileged <232> to accomplish the same thing. Make sure that the .COM file is protected <104> so users can execute it. Below is a sample .COM file: $_SET NOVERIFY $_SET NOON $WHO = F$USER $WHICH = F$JOB $_IF F$PRIV("NOSEND") THEN _GOTO CANT $_BROADCAST/BELL 'WHO' "Batch job ''WHICH' has completed" $_GOTO DONE $CANT: $_IF F$SEAR("MAIL$:MAIL.HLP") .EQS. "" THEN _GOTO NOMAIL $_SET DATA $_CCL SEND "Batch job ''WHICH' has completed" TO I Y $EOD $_GOTO DONE $NOMAIL: $_WRITE 0 "Couldn't notify user!" $DONE: $_LOGOUT/BRIEF $ ! Sample .COM file to notify user of batch completion. If the $ ! batch job has SEND priv, then we simply send a message. $ ! Otherwise, if the system has DECMAIL, we will MAIL ourself $ ! a message. Failing in that, we write a comment to the batch $ ! logfile saying we can't notify the user. 5 Example 3 - Changing one sub-option of a system command Example 3 - Changing one sub-option of a system command Example 3 - Changing one sub-option of a system command Example 3 - Changing one sub-option of a system command Requirement: Modify the SET HOST command so that issuing it requires the USER1 privilege. Frequently, it is desirable to modify the operation of one of the 'complex' DCL commands. A good example of a complex command is the SET command. This example shows how a Basic utility can be 'inserted' in the SET command processing. The system passes the SET command line to the Basic program, which then performs any desired inspection or modification and re- issues the command with a leading underscore to prevent infinite looping. This technique is very useful (as an example) for forcing a user to specify a command line option which SET usually defaults. This works because specifying a qualifier without a corresponding argument will cause DCL to prompt for the missing argument. 1000 EXTEND & ! SET.BAS - Example of a utility to trap a suboption of a complex & ! DCL command (in this case, SET HOST). & ! & ! V1.0-00 - 11-May-88 - tmk & ! 1010 CMD$=SYS(CHR$(7%)) & ! Get user's SET command & \ CMD2$=CVT$$(CMD$,32%) & ! Convert to upcase & \ I%=INSTR(1%,CMD2$,"/") & \ I%=LEN(CMD2$) IF I%=0% & ! Get location of first option, or length if none & \ J%=INSTR(1%,CMD2$," HO") & ! Get location of HOST option - attempt 1 & \ GOTO 1020 IF J%<>0% & ! If found as HO & \ J%=INSTR(1%,CMD2$," HO") & ! Get location of HOST option - attempt 2 & ! Note: The above code is "HO" 1020 GOTO 1030 IF J%=0% & \ GOTO 1030 IF J%>I% & ! Skip if no HOST found before 1st option & \ A$=SYS(CHR$(6%)+CHR$(32%)+CHR$(1%)+STRING$(3%,0%)+"USER1"+CHR$(0%)) & \ GOTO 1030 IF MID(A$,3%,1%)=CHR$(0%) & ! Check for USER1 priv. Proceed if present & \ PRINT "?SET HOST requires USER1 privilege."+CHR$(7%)+CHR$(13%) & \ GOTO 32767 & ! Else give the error message 1030 A$=SYS(CHR$(14%)+"$_"+CMD$) & ! Issue the command directly to DCL 32767 END 6 The above would be activated by placing the command: DEFINE/COMMAND/SYSTEM SE-T $SET.* /PRIV in the [0,1]START.COM file, and the command: SE*T == "CCL SET" in the system-wide LOGIN.COM file. Example 4 - Allow 'named' logins as well as by PPN Example 4 - Allow 'named' logins as well as by PPN Example 4 - Allow 'named' logins as well as by PPN Example 4 - Allow 'named' logins as well as by PPN Requirement: Enhance LOGIN to allow a username to be specified as well as a PPN. (This is known as 'VMS envy'). The example given here presumes that you have DECmail-11 for RSTS V3.0 or newer installed on your system. This allows you to use your DECmail-11 username as a login name. If you don't have DECmail-11, you can easily read a text file of username-to-PPN mappings instead. Working with a COPY of LOGIN.COM and an editor of your COPY COPY COPY choice, change the line number of line 13001 to 13008. Insert the following code immediately before that line: 13001 OPEN "MAIL$:NAMES.SYS" FOR INPUT AS FILE #2%, & RECORDSIZE 512%, MODE 8192% & \ FIELD #2%, 512% AS MAIWRK$ & \ LOGIN1$=CVT$$(LOGIN$,34%) & \ GOTO 13004 IF LOGIN1$="" & \ GET #2%, BLOCK 1% & \ MAIREC%=CVT$%(MID(MAIWRK$,5%,2%)) & ! OPEN THE MAIL NAMES FILE & ! CONVERT TYPED USERNAME TO UPCASE, NO SPACES OR TABS & ! GET THE FIRST RECORD OF MAIL USERNAMES & 13002 GET #2%, BLOCK MAIREC% & \ FOR MAITMP%=33% TO 512% STEP 16% & \ MAINAM$=CVT$$(MID(MAIWRK$,MAITMP%,12%),160%) & \ GOTO 13003 IF MAINAM$=LOGIN1$ & \ NEXT MAITMP% & \ MAIREC%=CVT$%(LEFT(MAIWRK$,2%)) & \ GOTO 13002 UNLESS MAIREC%=0% & \ GOTO 13004 & ! READ THE FIRST USERNAME BLOCK & ! LOOK AT ALL THE POSSIBLE NAMES IN IT & ! IF WE HAVE A MATCH, EXIT & ! ELSE LOOK AT REST & ! IF NOT FOUND, DETERMINE NEXT RECORD AND LOOP IF NOT & ! AT END & ! ELSE RETURN A NON-MATCH & 13003 LOGIN$="["+NUM1$(ASCII(MID(MAIWRK$,MAITMP%+12%,1%))) & +","+NUM1$(ASCII(MID(MAIWRK$,MAITMP%+13%,1%)))+"]" & ! CONSTRUCT PPN FROM MAIL USERNAME ENTRY & 7 13004 CLOSE #2% & ! CLOSE THE MAIL NAMES FILE & Change the start of line 13900 as follows: 13900 RESUME 13008 IF ERL=13001 & \ RESUME 13008 IF ERL=13002 & \ RESUME 19999 IF KB.SPAWNED% OR (NOT (LOGGED.IN%)) & OR A%=0% & \ PRINT "?Invalid entry - try again" IF A% & Once you have entered the patches into your copy of LOGIN.BAS, compile it with Basic-Plus 2 if you have it, as shown below: $ RUN $BP2IC2 SCALE 0 OLD LOGIN.BAS COMPILE LOGIN.OBJ/OBJ/CHA/LIN/NODEB/WOR/NOCRO/NOLIS/FLAG:NODEC $ SW DCL $ TKB LOGIN.TSK/FP=LOGIN.OBJ,LB:BP2OTS.OLB/LB / UNITS=13 ASG=SY:5:6:7:8:9:10:11:12 EXTTSK=512 LIBR=CSPLIB:RO // $ DELETE/NOLOG LOGIN.OBJ Note that Basic-Plus 2 V2.4 has a bug which prevents it from compiling LOGIN, with or without these modifications. Therefore, you must use either V2.3 or V2.5. If you don't have BP2, you can use Basic-Plus. CSPCOM will not work, so don't use it! Now, log onto your system from at least two terminals (so you can recover if something goes wrong). Rename $LOGIN.TSK to $LOGIN.OLD, and copy the new LOGIN program (either LOGIN.TSK for BP2 or LOGIN.BAC for B+) to $. Ensure it has a protection code of <232>. Next, ensure that the DECmail-11 NAMES.SYS file is readable by all users: PIP MAIL$:NAMES.DAT<40>/RE Basic installation is now complete. You may now enter any valid MAIL username when logged-out, and then provide the password in the usual manner. If you want this new feature to work when logged-in also, you will need to make some additional changes because DCL will try to outsmart you by not allowing non-numeric user information in the DCL LOGIN command. You can 8 cure this by inserting the following two lines in their respective files: In [0,1]START.COM: $ define/command/system LOG-IN $LOGIN.* /privilege In [0,1]LOGIN.COM: $ LOG-IN == "CCL LOGIN" Note that this will 'break' the DCL command LOGIN/TERMINAL. To work around this, simply put an underscore in front of that command wherever it is used, as so: _LOGIN/TERMINAL=KB1: [1,2]. This problem only happens when you add the two lines for logged-in name translation. Name translation will not work in DECNET requests for access information, as they are not handled by LOGIN. Example 5 - Helper .TSK file number one Example 5 - Helper .TSK file number one Example 5 - Helper .TSK file number one Example 5 - Helper .TSK file number one Requirement: A site has many RSTS systems of varying vintages, and wants to have a common backup procedure to back up all the disks on a system to tape. The problem is that one system has RA81's and a TU80, and another has RK07's and a TE16. If we had a list of all the disk devices and their pack ID's, a simple .COM file could be constructed which would prompt the operator for each disk. The trick is to fish around in the RSTS/E Internals Manual (either DEC's or Mike Mayfield's) to determine where the system keeps this information. Once we have that information, we can write a program like this: 1000 EXTEND & ! DSKTBL.BAS - Return first available tape unit and all mounted & ! disk units in DCL symbol DSKTBL. & ! & ! V1.0-00 - 11-May-88 - tmk & ! 1010 DIM M%(30%),M2%(30) 1020 CHANGE SYS(CHR$(6%)+CHR$(-3%)) TO M% & \ M%(J%)=M%(J%)+SWAP%(M%(J%+1%)) FOR J%=5% TO 29% STEP 2% & \ CHANGE SYS(CHR$(6%)+CHR$(-12%)) TO M2% & \ M2%(J%)=M2%(J%)+SWAP%(M2%(J%+1%)) FOR J%=3% TO 29% STEP 2% 1030 DEVCNT%=M%(5%) & \ UNTCNT%=M%(19%) & \ DEVNAM%=M2%(5%) & \ NUM.DISK.TYPES%=M2%(9%) & \ DEV.IDX%=0% 1040 NXTDEV$=CVT%$(SWAP%(PEEK(DEVNAM%+NUM.DISK.TYPES%+DEV.IDX%))) & \ GOTO 1050 IF NXTDEV$="TT" & ! If got to TT (1st synonym), no tapes present & 9 \ GOTO 1060 IF LEFT(NXTDEV$,1%)="M" & ! If we have a tape, that's all we need to know... & \ DEV.IDX%=DEV.IDX%+2% & \ GOTO 1040 1050 NXTDEV$="XX" & ! Indicate impossible device 1060 DSKTBL$=NXTDEV$+"0:" & ! Store tape unit name & \ DSKLOG%=M2%(21%) & \ FUN% = 0% & \ FOR C%=0% TO NUM.DISK.TYPES%-2% STEP 2% & \ DEVICE.NAME$=CVT%$(SWAP%(PEEK(DEVNAM%+C%))) & \ FOR U%=0% TO PEEK(DEVCNT%+C%) & \ UNTCNT.ENTRY%=PEEK(UNTCNT%+FUN%) & \ DSKLOG.PTR%=DSKLOG%+(FUN%*5%) & \ GOTO 1070 IF UNTCNT.ENTRY%<0% & ! No more units for device type & \ GOTO 1070 IF (UNTCNT.ENTRY% AND 4096%)<>0% & ! Mounted non-file-structured & \ GOTO 1070 IF DEVICE.NAME$="DV" & ! Omit virtual disk, too & \ DSKTBL$=DSKTBL$+DEVICE.NAME$+NUM1$(U%)+":"+ & RAD$(PEEK(DSKLOG.PTR%))+RAD$(PEEK(DSKLOG.PTR%+2%))+ & RAD$(PEEK(DSKLOG.PTR%+4%)) & ! Else add to disk name string 1070 FUN% = FUN%+2% & \ NEXT U% & \ NEXT C% & \ A$=SYS(CHR$(14%)+"$DSKTBL="+CHR$(34%)+DSKTBL$+CHR$(34%)) 32767 END First, the program looks a non-disk devices to see if there is a tape drive on the system. The first tape drive found will be used for the backup. If there is no tape drive, we will find the 'TT' synonym for keyboards. In that case, we set an impossible device name as a flag for the .COM file and continue. Next, the program traverses the disk device table looking for disks which are configured on the system. It then verifies that the unit number we are looking at exists and is mounted (and not mounted non-file-structured). If this requirement is met and the disk is not the virtual disk, the device name and pack name are appended to the string. When there are no more disk units, the string is appended to the text: $DSKTBL= which is then issued as a CCL command. Since $ is always a legal CCL, the entire command is passed to DCL which will create a symbol called DSKTBL, containing the information we want. The following .COM file uses this information to prompt the operator for volumes to be backed up: 10 $ ! BAKDSK.COM - Do a BACKUP of all disks on the system (that we know about) $ ! 11-May-88 - Version to user 'helper' .TSK for device names $ _on control_c then goto escape $ _set nodata $ _set noon $ _ope/log/rep BAKDSK.LOG $ _write 0 "This procedure will back up all of the disks onto tape." $ _write 0 "It is suggested that you do a PURGE before proceeding." $ _inquire/exit=byebye isgood "Do you wish to continue? (/N)" $ _if f$left(f$edit(isgood,38),1) .eqs. "N" then _goto byebye $ _write 0 "" $ _run TOOLS:DSKTBL $ _if f$left(dsktbl,1) .nes. "M" then _goto notape $ tapdev=f$left(dsktbl,4) ! name of tape drive on system $ dsktbl=f$mid(dsktbl,5,f$len(dsktbl)) $nxtdsk: $ _if f$len(dsktbl) .eq. 0 then _goto alldun $ dskdev=f$left(dsktbl,4) ! device name of current disk $ dsknam=f$mid(dsktbl,5,9) ! full name (9 chars) for mounted pack $ dsknam=f$edit(dsknam,38) ! trim trailing spaces $ dskna6=f$left(dsknam,6) ! short name for backup set ID $ dsktbl=f$mid(dsktbl,14,f$len(dsktbl)) $ _inquire/exit=byebye nxtdsk - "Do you wish to back up ''dskdev'''dsknam'? (/N)" $ if f$left(f$edit(nxtdsk,38),1) .eqs. "N" then goto nxtdsk $ _backup/account_data/in_label='dsknam'/initialize/listfile= - 'dskna6'.LST/out_label='dskna6'/rewind/end=nodismount - 'dskdev'[*,*]*.* 'tapdev''dskna6'.BCK $ _copy 'dskna6'.LST 'tapdev' $ _delete 'dskna6'.LST $ _dismount 'tapdev'/unload $ _write 0 "Please dismount this volume." $ _if $severity .ge. 2 then goto isitok $ _goto nxtdsk $alldun: $ _write 0 "" $ _write 0 "The backup procedure is now complete." $byebye: $ _close/log $ _print/queue=LP0: BAKDSK.LOG $ _exit $isitok: $ _write 0 "Please inspect the console log for fatal errors and" $ _write 0 "retry this volume if required." $ _goto nxtdsk $escape: $ _set noon $ _write 0 "?Aborted by Control-C" $ _goto byebye $notape: $ _write 0 "?No tape units configured on system - cannot continue." $ _goto byebye 11 Example 6 - Helper .TSK file number two Example 6 - Helper .TSK file number two Example 6 - Helper .TSK file number two Example 6 - Helper .TSK file number two Requirement: The same site as in the previous example has a number of RSTS systems, all DECnetted together. The site wants to have a common START.COM system startup file, with conditional sections for system-unique items. The 'obvious' answer to this is to use the system logical F$NODE() to get the DECnet/E node name. Unfortunately, this logical is not defined until the DECnet/E is started. Therefore, it is unavailable at the critical early stages of system startup. Therefore, the following program was written to look in the DECnet/E permanent parameter file and return the executor node name defined there: 1000 EXTEND & ! GETNOD.BAS - Return the system's DECNET node name, *even* if DECnet/E & ! is not currently running. The node name (or null if no & ! network is configured) is returned in DCL symbol FNODE & ! & ! V1.0-00 - 11-May-88 - tmk & ! 1010 ON ERROR GOTO 1030 1020 OPEN "[0,1]NETPRM.SYS" FOR INPUT AS FILE #1%, RECORDSIZE 512% & \ FIELD #1, 2% AS JUNK1$, 2% AS EXEC$, 508% AS JUNK2$ & \ GET #1%, RECORD 1% & ! Get header record & \ EXEC%=SWAP%(CVT$%(EXEC$))+1% & ! Extract pointer to Executor definition record & \ FIELD #1, 84% AS JUNK1$, 6% AS NODNAM$, 44% AS JUNK2$, & 17% AS NETFIL$, 361% AS JUNK3$ & \ GET #1%, RECORD EXEC% & ! Get Executor definition record & \ GOTO 1040 IF NETFIL$<>"SY0:[0,1]NSP0.SYS" & ! Verify we're in the right place & \ GOTO 1050 1030 NODNAM$="" & ! If no permanent parameter file, no node name & \ RESUME 1050 1040 PRINT "%Node identification may be inaccurate." 1050 A$=SYS(CHR$(14%)+"$FNODE="+CHR$(34%)+NODNAM$+CHR$(34%)) 32767 END Again, a symbol (FNODE) is returned containing the current executor node name. The check for NSP0.SYS is a 'sanity check' on the location of the node name information. Since the format of internal system information is undocumented by DEC, it is wise to include as many of these checks as needed, in the event that DEC changes the structure of the file. 12 Example 7 - DECmail-11 'directory assistance' Example 7 - DECmail-11 'directory assistance' Example 7 - DECmail-11 'directory assistance' Example 7 - DECmail-11 'directory assistance' Requirement: Implement a user directory function for DECmail-11. It should include the mail name, full user name, department, title, phone number and comment fields. DECmail-11 uses a text file in the MAIL$: account named NAMES.DAT as an ASCII list of usernames. The DECmail NAMES program converts this list into a binary file for use by MAIL. One of the features of the NAMES program is the removal of comment fields in the NAMES.DAT file. Therefore, we can use the NAMES file to contain the additional information as a comment. A sample NAMES.DAT file follows: ! DECmail-11 user names - 21-Jan-87 - tmk ! Format of comment field is !department,title,phone,comment ! ALL commas must be in place or INFO will blow up! [1,4],DEC Field Service,FIELD !System support,,N/A, [1,245],Paul Greene,PAUL !Academic Computer Services,,X9381, [1,247],Richard Labov,RLABOV !Off-campus,,N/A, [1,253],Ben Cohen,BEN !Academic Computer Services,,X9381, [1,254],Terry Kennedy,TERRY !Academic Computer Services,Systems Manager,X283, [1,254],Information,INFO !MAIL information & help,,X9381, [10,1],Generic MAIL sender,MAIL !Generic MAIL sender,,,Cannot receive MAIL [10,2],SIS MAIL Test,SIS_TEST !SIS MAIL test account,,, The INFO program processes this information to produce reports line the ones shown: $ info Info> dir Directory of users on local node UserID Full username Account Department ------------ ---------------------------- --------- --------------------------- FIELD DEC Field Service [1,4] System support PAUL Paul Greene [1,245] Academic Computer Services RLABOV Richard Labov [1,247] Off-campus BEN Ben Cohen [1,253] Academic Computer Services TERRY Terry Kennedy [1,254] Academic Computer Services INFO Information [1,254] MAIL information & help MAIL Generic MAIL sender [10,1] Generic MAIL sender SIS_TEST SIS MAIL Test [10,2] SIS MAIL test account End of requested listing. Info> who mail UserID: MAIL Full username: Generic MAIL sender Department: Generic MAIL sender Title: Phone no.: Account: [10,1] Comment field: Cannot receive MAIL 13 Last login: 05-May-88 01:46 PM Auto answer message: The account you are sending mail to is a send-only account, and therefore cannot receive the mail you have just sent to it. Please use alternate means of contacting the individual. Info> search computer Listing of matching users on local node UserID Full username Account Department ------------ ---------------------------- --------- --------------------------- PAUL Paul Greene [1,245] Academic Computer Services BEN Ben Cohen [1,253] Academic Computer Services TERRY Terry Kennedy [1,254] Academic Computer Services End of requested listing. Info> ^Z As you can see, there are several report formats. Additionally, the date/time of last login and any DECmail auto- answer message are displayed. Here are the files required to build the INFO task: 10 EXTEND 20 ! INFO.BAS - 1.00-00 02-Nov-87 - tmk - Directory pgm for DECMAIL-11 & ! 1.10-01 05-Nov-87 - tmk - Add CCL entry, command args, & ! better help & ! 1.20-02 17-Nov-87 - tmk - Move substringing into subrou- & ! tine, add DECNET/E support & ! & ! NOTICE - See the comments at line 11000 regarding DECnet/E support & ! before compiling or installing this software. & ! & 100 ON ERROR GOTO 32400 ! SET STANDARD ERROR TRAP & \ PRINT "MAIL directory program V1.20-02 - 17-Nov-87 - tmk" & \ PRINT "" & \ PRINT "Enter 'HELP' at the Info> prompt for help." & 110 OPEN "_KB:INFO.CMD" AS FILE #2% & \ NODE$="" ! DEFAULT TO LOCAL NODE & 120 PRINT & \ GOTO 32760 IF CCLENT%<>0% & \ CCLENT%=0% 125 PRINT "Info> "; ! PRINT PROMPT & \ INPUT LINE #2%, CMND$ ! GET COMMAND 130 CLOSE #1% ! CLOSE MAIL NAMES FILE 140 CMND$=CVT$$(CMND$,189%) ! MAKE IT U/C & \ GOTO 125 IF CMND$="" & \ GOTO 125 IF CMND$=" " & \ ARG$="" & \ I%=INSTR(1%,CMND$," ") ! ANY ARGUMENTS? & 14 \ GOTO 150 IF I%=0% ! IF NOT & \ ARG$=MID(CMND$,I%+1%,LEN(CMND$)) 150 GOTO 1000 IF LEFT(CMND$,1%)="H" & \ GOTO 2000 IF LEFT(CMND$,1%)="S" & \ GOTO 3000 IF LEFT(CMND$,1%)="D" & \ GOTO 4000 IF LEFT(CMND$,1%)="W" & \ GOTO 5000 IF LEFT(CMND$,1%)="V" & \ GOTO 6000 IF LEFT(CMND$,1%)="N" & \ GOTO 32760 IF LEFT(CMND$,1%)="Q" & \ PRINT "Unknown command '";CMND$;"'." & \ GOTO 120 & 1000 ! HELP FUNCTION & ! & PRINT "" & \ PRINT "INFO is a program which allows you to look up users in the MAIL" & \ PRINT "database on this system or on other RSTS/E systems (if you have" & \ PRINT "the DECnet/E package) by a number of options. The options which" & \ PRINT "are available are:" & \ PRINT "" & \ PRINT " SEARCH - Locate a person by department, title, name, etc." & \ PRINT " You enter the text you want to look for. All rec-" & \ PRINT " ords which match will be displayed. " & \ PRINT " DIRECTORY - Display the entire user directory on the terminal." & \ PRINT " WHO - Display detailed information about a specific user" & \ PRINT " on the terminal. This is the only way to view the" & \ PRINT " entire data for a user." & \ PRINT " NODE - Allows you to specify a different RSTS/E system to" & \ PRINT " look up users on (if you have the DECnet/E package" & \ PRINT " installed). By default, lookups are done on your" & \ PRINT " computer system." & \ PRINT " VERSION - Display the version number of this program." & \ PRINT " QUIT - Exit from this program." & \ PRINT "" & \ PRINT "If you wish to update your own information, simply send MAIL to" & \ PRINT "INFO detailing the changes you want made. Similarly, department" & \ PRINT "chairs may request the addition or deletion of faculty member's" & \ PRINT "information." & \ GOTO 120 & 2000 ! SEARCH FUNCTION & ! & SCAN$=ARG$ & \ PRINT "Search string? "; IF SCAN$="" & \ INPUT LINE #2%,SCAN$ IF SCAN$="" & \ SCAN$=CVT$$(SCAN$,37%) & \ GOSUB 11000 ! OPEN NAMES FILE 2010 LIN%=0% & \ PRINT "Listing of matching users on "; & \ PRINT "local node" UNLESS NODE$<>"" & \ PRINT "node ";NODE$;"::" IF NODE$<>"" & \ PRINT "UserID Full username Account "+ & "Department" & 15 \ PRINT "------------ ---------------------------- --------- "+ & "---------------------------" 2020 GOSUB 10000 ! GET RECORD & SNIP & \ GOTO 2030 IF SPLIT1%=0% ! ON EOF & \ GOTO 2020 IF INSTR(1%,CVT$$(DATUM$,32%),SCAN$)=0% & \ PRINT U.ID$; & \ PRINT TAB(13%);U.NAME$; & \ PRINT TAB(42%);U.ACCT$; & \ PRINT TAB(52%);U.DEPT$ & \ LIN%=LIN%+1% & \ GOTO 2020 IF LIN%<19% ! MORE ROOM ON SCREEN & \ PRINT "Press [RETURN] for next screen or any key to exit..." & \ INPUT #2%, CMND$ & \ GOTO 2010 IF CMND$="" & \ GOTO 120 2030 PRINT "End of requested listing." & \ GOTO 120 & 3000 ! DIRECTORY FUNCTION & ! & GOSUB 11000 ! OPEN NAMES FILE 3005 LIN%=0% & \ PRINT "Directory of users on "; \ PRINT "local node" UNLESS NODE$<>"" & \ PRINT "node ";NODE$;"::" IF NODE$<>"" & \ PRINT "UserID Full username Account "+ & "Department" & \ PRINT "------------ ---------------------------- --------- "+ & "---------------------------" 3010 GOSUB 10000 ! GET RECORD & SNIP & \ GOTO 3020 IF SPLIT1%=0% ! ON EOF & \ PRINT U.ID$; & \ PRINT TAB(13%);U.NAME$; & \ PRINT TAB(42%);U.ACCT$; & \ PRINT TAB(52%);U.DEPT$ & \ LIN%=LIN%+1% & \ GOTO 3010 IF LIN%<19% ! MORE ROOM ON SCREEN & \ PRINT "Press [RETURN] for next screen or any key to exit..." & \ INPUT #2%, CMND$ & \ GOTO 3005 IF CMND$="" & \ GOTO 120 3020 PRINT "End of requested listing." & \ GOTO 120 & 4000 ! WHO FUNCTION (DOCTOR, DOCTOR...) & ! & USERID$=ARG$ & \ PRINT "UserID? "; IF USERID$="" & \ INPUT LINE #2, USERID$ IF USERID$="" & \ USERID$=CVT$$(USERID$,37%) & \ GOSUB 11000 ! OPEN NAMES FILE 4010 GOSUB 10000 ! GET RECORD AND SNIP & \ GOTO 4060 IF SPLIT1%=0% ! IF EOF & 16 \ GOTO 4010 IF USERID$<>MID(DATUM$,SPLIT2%+1%,SPLIT3%-SPLIT2%-1%) & \ PRINT "UserID: ";TAB(15%); & \ PRINT NODE$+"::"; IF NODE$<>"" & \ PRINT U.ID$ & \ PRINT "Full username: ";TAB(15%);U.NAME$ & \ PRINT "Department: ";TAB(15%);U.DEPT$ & \ PRINT "Title: ";TAB(15%);U.TITLE$ & \ PRINT "Phone no.: ";TAB(15%);U.PHONE$ & \ PRINT "Account: ";TAB(15%);U.ACCT$ & \ PRINT "Comment field: ";TAB(15%);U.COMNT$ & \ PRINT "Last login: ";TAB(15%);U.LSTLG$ \ GOTO 120 IF NODE$<>"" ! NO AUTOANSWER IF REMOTE NODE 4020 FILNAM$=U.ACCT$+"AUTOAN.MSG" & \ OPEN FILNAM$ FOR INPUT AS FILE #3% & \ PRINT "" & \ PRINT "Auto answer message:" 4030 INPUT LINE #3%, MSG$ & \ PRINT MSG$; & \ GOTO 4030 4040 CLOSE #3% 4050 GOTO 120 4060 PRINT "UserID not found." & \ GOTO 120 & 5000 ! VERSION FUNCTION & ! & PRINT "INFO V1.20-02 - 17-Nov-87 - tmk" & \ GOTO 120 & 6000 ! NODE FUNCTION & ! & NODE$=ARG$ & \ PRINT "Node? "; IF NODE$="" & \ INPUT LINE #2, NODE$ IF NODE$="" & \ NODE$=CVT$$(NODE$,37%) & \ GOTO 120 & 10000 INPUT LINE #1%,DATUM$ & \ U.DEPT$="" & \ U.TITLE$="" & \ U.PHONE$="" & \ U.COMNT$="" & \ DATUM$=LEFT(DATUM$,LEN(DATUM$)-2%) & \ GOTO 10000 IF LEFT(DATUM$,1%)="!" & \ SPLIT1%=INSTR(1%,DATUM$,"],")+1% & \ SPLIT2%=INSTR(SPLIT1%+1%,DATUM$,",") & \ SPLIT3%=INSTR(SPLIT2%+1%,DATUM$,CHR$(9%)) & \ SPLIT3%=LEN(DATUM$)+1% IF SPLIT3%=0% & \ SPLIT4%=INSTR(SPLIT3%+1%,DATUM$,"!") & \ SPLIT5%=INSTR(SPLIT4%+1%,DATUM$,",") & \ SPLIT6%=INSTR(SPLIT5%+1%,DATUM$,",") & \ SPLIT7%=INSTR(SPLIT6%+1%,DATUM$,",") & \ U.ID$=MID(DATUM$,SPLIT2%+1%,SPLIT3%-SPLIT2%-1%) & 17 \ U.NAME$=MID(DATUM$,SPLIT1%+1%,SPLIT2%-SPLIT1%-1%) & \ U.ACCT$=LEFT(DATUM$,SPLIT1%-1%) & \ GOTO 10010 IF SPLIT3%=LEN(DATUM$)+1% & \ U.DEPT$=MID(DATUM$,SPLIT4%+1%,SPLIT5%-SPLIT4%-1%) & \ U.TITLE$=MID(DATUM$,SPLIT5%+1%,SPLIT6%-SPLIT5%-1%) & \ U.PHONE$=MID(DATUM$,SPLIT6%+1%,SPLIT7%-SPLIT6%-1%) & \ U.COMNT$=MID(DATUM$,SPLIT7%+1%,80%) 10010 U.LSTLG$="" & \ U.LSTLG$="" IF NODE$<>"" & \ GOTO 10020 IF NODE$<>"" ! IF NOT SAME NODE, NO LAST LOGIN & \ SPLIT1A%=INSTR(1%,DATUM$,",") & \ PROJ%=VAL(MID(DATUM$,2%,SPLIT1A%-2%)) & \ PROG%=VAL(MID(DATUM$,SPLIT1A%+1%,SPLIT1%-SPLIT1A%-2%)) & \ MSG$=SYS(CHR$(6%)+CHR$(-25%)+CHR$(-1%)+CHR$(4%)+CHR$(PROG%)+ & CHR$(PROJ%)) & \ GOTO 10020 IF CVT$%(MID(MSG$,9%,2%))=0% & \ U.LSTLG$=DATE$(SWAP%(CVT$%(MID(MSG$,9%,2%))))+ & " "+TIME$(SWAP%(CVT$%(MID(MSG$,11%,2%))) AND 2047%) 10020 RETURN ! RETURN WITH RECORD 10030 SPLIT1%=0% ! ERROR TRAP & \ RETURN ! RETURN EOF & 11000 ! OPEN NAMES FILE ON DESIGNATED NODE & ! & ! Note - If you don't have BP2 or DECnet/E, replace this whole & ! subroutine with: & ! OPEN "MAIL$:NAMES.DAT" FOR INPUT AS FILE #1% & ! If you have DECnet/E, you must change the data: & ! "[2,2] FOOBAR BARFOO" to the account and passwords for your & ! default DECnet account on the remote node. N.B. All remote & ! nodes must have the same account/password information. & ! & MAINAM$="MAIL$:NAMES.DAT" & \ MAINAM$=NODE$+'"[2,2] FOOBAR BARFOO"::'+MAINAM$ IF NODE$<>"" & \ OPEN MAINAM$ FOR INPUT AS FILE #1%, ORGANIZATION SEQUENTIAL, & ACCESS READ & \ RETURN ! FILE OPENED, EXIT 11010 GOTO 11020 IF NODE$="" ! IF HAPPENED HERE & \ PRINT "?"+NODE$+":: is an unknown or unreachable node, or the login" & \ PRINT " information provided was invalid at the remote node." & \ NODE$="" ! COULDN'T OPEN, KILL NODE & \ GOTO 120 ! AND DO WITHOUT 11020 PRINT "?Unable to access MAIL$:MAIL.SYS file. Please contact" & \ PRINT " your system managment personnel or support staff." & \ GOTO 32767 & 30000 ! CCL ENTRY & ! & ON ERROR GOTO 32400 & \ OPEN "_KB:INFO.CMD" AS FILE #2% & \ NODE$="" ! DEFAULT TO LOCAL NODE & \ CMND$=SYS(CHR$(7%)) ! GET CORE COMMON & \ CMND$=CVT$$(CMND$,189%) ! CONVERT IT & 18 \ I%=INSTR(1%,CMND$," ") ! ANY OPTIONS? & \ GOTO 120 IF I%=0% ! NOPE, CONTINUE & \ CMND$=MID(CMND$,I%+1%,LEN(CMND$)) & \ CCLENT%=1% & \ GOTO 130 & 32400 ! ERROR TRAP & ! & RESUME 140 IF ERL=130% ! UNABLE TO CLOSE NAMES FILE & \ RESUME 10030 IF ERL=10000% ! EOF ON NAMES FILE & \ RESUME 10020 IF ERL=10010% ! NO LAST LOGIN INFO & \ RESUME 11010 IF ERL=11000% ! BAD NODENAME & \ RESUME 4050 IF ERL=4020% ! NO AUTOAN.MSG FILE & \ RESUME 4040 IF ERL=4030% ! EOF ON AUTOAN.MSG & \ RESUME 32760 IF ERL=125% ! EOF AT MAIN PROMPT & \ RESUME 120 IF ERR=11% ! EOF ON USER INPUT & \ RESUME 32750 ! UNKNOWN ERROR & 32750 PRINT "?Received error ";NUM1$(ERR);" at line ";NUM1$(ERL);"." 32760 ON ERROR GOTO 32767 & \ CLOSE #1% 32767 END ! INFO.CMD - INFO taskbuilder command file SY:INFO/FP=SY:INFO/MP UNITS = 13 ASG = SY:5:6:7:8:9:10:11:12 CLSTR = DAPRES,RMSRES:RO // ! INFO.ODL - INFO taskbuilder .ODL file .ROOT BASIC2-RMSROT-USER,RMSALL USER: .FCTR SY:INFO-LIBR LIBR: .FCTR LB:BP2OTS/LB @LB:BP2IC7 @LB:DAPRLX .END $! INFO.COM - Command file to build INFO $SWITCH DCL $SET VERIFY $SET NOON $RUN $BP2IC2 SCALE 0 OLD INFO.BAS SET SEQ ODLRMS LB:DAPRLX COMPILE INFO.OBJ/OBJ/CHA/LIN/NODEB/WOR/NOCRO/NOLIS/FLAG:NODEC $SW DCL $TKB @INFO 19 Appendix 1 - SCAN program listing Appendix 1 - SCAN program listing Appendix 1 - SCAN program listing Appendix 1 - SCAN program listing 1000 EXTEND 1010 ON ERROR GOTO 32000 & \ PRINT "Binary file search 1.20 - 16-Nov-87 KS" & \ PRINT 1020 PRINT "File"; 1030 INPUT LINE F1$ & \ F1$=MID(F1$,1%,LEN(F1$)-2%) 1040 PRINT "Search string"; & \ INPUT LINE S$ & \ S$ = LEFT(S$, LEN(S$) - 2%) 1050 OPEN F1$ FOR INPUT AS FILE #1%, RECORDSIZE 1024% 1060 FIELD #1, 511% + LEN(S$) AS I1$ & \ I%=1% 1070 GET #1%, BLOCK I% & \ ST% = 1% 1075 BYT%=INSTR(ST%,I1$,S$) & \ GOTO 1080 IF BYT%=0% & \ PRINT "Found in block ";NUM1$(I%);" ("; & \ F.NUM% = I% - 1% & \ GOSUB 11000 & \ F.NUM% = BYT% - 1% & \ PRINT "), offset ";NUM1$(F.NUM%);" ("; & \ GOSUB 11000 & \ PRINT ")." & \ ST% = BYT% + LEN(S$) & \ GOTO 1075 1080 I%=I%+1% & \ GOTO 1070 & 11000 REM SUBROUTINE TO PRINT IN OCTAL 11010 IF F.NUM% < 0% & THEN F.I$ = "1" & ELSE F.I$ = "0" & 11020 F.I$ = F.I$ + CHR$( (((F.NUM% AND 16384%) <> 0%) AND 4%) & +(((F.NUM% AND 8192%) <> 0%) AND 2%) & +(((F.NUM% AND 4096%) <> 0%) AND 1%) + 48%) & + CHR$( (((F.NUM% AND 2048%) <> 0%) AND 4%) & +(((F.NUM% AND 1024%) <> 0%) AND 2%) & +(((F.NUM% AND 512%) <> 0%) AND 1%) + 48%) & + CHR$( (((F.NUM% AND 256%) <> 0%) AND 4%) & +(((F.NUM% AND 128%) <> 0%) AND 2%) & +(((F.NUM% AND 64%) <> 0%) AND 1%) + 48%) & + CHR$( (((F.NUM% AND 32%) <> 0%) AND 4%) & +(((F.NUM% AND 16%) <> 0%) AND 2%) & +(((F.NUM% AND 8%) <> 0%) AND 1%) + 48%) & + CHR$( (F.NUM% AND 7%) + 48%) & \ PRINT F.I$; & \ RETURN & 32000 IF ERL=1050 THEN PRINT "?That file does not exist - try again..." & 20 \ RESUME 1020 32010 IF ERL=1070 THEN PRINT "Done..." & \RESUME 32767 32020 IF ERL=1020 THEN RESUME 32767 32030 IF ERL=1030 THEN RESUME 32767 32040 IF ERL=1040 THEN RESUME 32767 32050 PRINT "Received error ";NUM1$(ERR);" at line ";NUM1$(ERL);"." & \ RESUME 32767 & 32767 END Appendix 2 - How to reach the RSTS Newsletter System Appendix 2 - How to reach the RSTS Newsletter System Appendix 2 - How to reach the RSTS Newsletter System Appendix 2 - How to reach the RSTS Newsletter System The entire text of this article, as well as the related programs in both source and binary form are on the RSTS SIG Newsletter system in account [49,1]. To reach the system, dial (201) 915-9361 at either 300 or 1200 baud. When you get the carrier tone, press a few RETURNs until you get the User: prompt. Then enter 2,1 and press RETURN. You will then be logged into a guest account and can access these examples. In addition, the most recent RSTS SIG tapes are available on-line. Why is it called the Newsletter System, you ask? It is the preferred method for submitting articles to the RSTS SIG section of the DECUS US Chapter SIGs Newsletter. For more information on Newsletter subscriptions, contact: DECUS Subscription Service 291 Boston Post Road Marlboro, MA 01752-1850 (617) 480-3418 21