!TITLE Editing the configuration file ! ! %externalroutine modftrans ( %string(31) FILE ) ! ! To use modftrans, type modftrans <filename> ,where <filename> is the name of a !new file, or a configuration file previously created with this program. If a !file that was not created with this program is supplied, the program will !terminate saying so. ! You will be told if the file is a new file ( <filename> is a new file ); if !it isn't, ignore the message ! CONFIG#WORK is a copy of <filename> !This is output by the routine that copies the original file to a workfile !CONFIG#WORK. ! The program then prints out the first descriptor in the file ( see the !description of the command P for Print ), and gives the prompt ! Config : !to which you reply with a command. ! ! ! ! !< Commands !< Help !? ( for help ) !-------------- ! ! Type ? for help information. This gives a list of the commands available, !along with a ( very ) brief description of what they do. It also gives the legal !commands when inserting a descriptor ( more about this under the commands I and !C ). ! ! !> !< Defaults !! ( to print out the defaults for a descriptor ) !------------------------------------------------ ! ! The command ! prints out a list of the default settings for a descriptor. !The command takes no parameters. ! ! !> !< Move !M ( for Move ) !-------------- ! ! M moves to the next descriptor. Note that you can only move, insert, !delete etc in whole descriptors, not parameter by parameter. !If M is not followed by any parameters, the movement is forward by 1 descriptor. !However it is possible to suffix the command by an integer ( non-zero, but !negative numbers are allowed; they move backwards ). Instead of using a number, !you can suffix the command with * ( or -* ), meaning a very large number - to !the end ( or beginning ) of the file. If the move would take you off either !end of the file, you stop at the end. ! This command causes the position in the file to be printed out after the !command line has been executed. !> !< Print !P ( for Print ) !--------------- ! ! P prints out descriptors in the file. The same notes and comments apply as for !M: you can print out n, -n, '*' or '-*' descriptors, and you can't print out off !either end of the file. Also, the program tells you if you are at the !beginning or the end of the file, with the message ! ** First station ** or ! ** Last station ** !Note that to get the latter message, you must be AFTER the last descriptor, !which will be a blank descriptor ( no parameters ). ! ! !> !< Insert !I ( for Insert ) !---------------- ! ! I allows you to insert descriptors into the file. You follow the command with !with the number of descriptors you want to insert ( not zero or negative, 1 by !default ). You can't have more than 512 stations, so if you try to insert a !number that would give a total of more than this, the number is trimmed, and !you are told how many may be input. ! For each parameter of a descriptor, you are told the default, if one exists. !You are then prompted for the value or setting of the parameter. Legal replies !are : <return> means leave as defaulted ( if there is no default, the program ! asks for another reply ) ! * means leave this and all the other parameters as defaulted ! ( if either this parameter or any following ones have no ! default, the program asks for another reply ); ! ? means 'help' - the program supplies a brief description of the ! parameter, and perhaps possible settings; ! <text> means set the parameter to <text>. !PAGE ! ! If either the descriptor is longer than 4096 bytes, or the total file length !with the new descriptor is longer than 200 Kbytes, the insertion is aborted with !the message ! Descriptor too large ! ! !> !< Delete !D ( for Delete ) !---------------- ! ! You can delete descriptors from the file with the command D. Legal parameters !are n or * ( or 1 by default ); you can't delete backwards. The best way to !delete -n descriptors is to set up the macro 'M-D' ( see under command U ) and !then give the command Xn ( Execute macro n times ); DON'T use M-nDn, since if !you hit the top of the section prematurely, you will still delete n descriptors, !which will be more than intended. ! This command causes the position in the file to be printed out when the !command line has been executed. !> !< Change !C ( for Change ) !---------------- ! ! Using the command C allows you to edit ( change ) a descriptor parameter by !parameter, instead of having to use DI. The parameters are the same as for !D : n or *, or 1 by default. This is the only command that doesn't strictly work !in whole descriptors, but in parameters. ! For each parameter, the program displays the current setting, then prompts you !with the name of the parameter. The legal replies are : ! <return>, meaning 'leave this parameter as it is', ! <*>, meaning 'leave the rest of the descriptor as it is', ! and <text>, meaning 'set this parameter to <text>'. ! Again, like I, if either the descriptor is longer than 4 Kbytes, or the total !file length longer than 200 Kbytes, the change is aborted with the message ! Descriptor too large. ! To simulate the command C-n, set up the macro M-CM- ( move backwards 2 since !the command C moves you onto the next descriptor after changing one ), then give !the command Xn. ! This command is one that causes the position in the file to be printed out !when the command line has been executed. !> !< Find !F ( for Find ) !______________ ! ! F allows you to move to the first descriptor ( in either direction ) that !contains a certain string. The legal formats of the command are : ! F/text/ means search for the first occurrence of the string 'text' in ! the file and stops at the beginning of the descriptor ! containing it ( remember the program only works in whole ! descriptors ), ! F-/text/ searches backwards for the last occurrence of 'text' in the ! same manner, ! F and F- search forwards and backwards respectively for the last used ! text ( which must have been defined ), ! F? prints out the current search text, or 'Not defined' if not ! defined. !In any format, if the text is not found, the message ! String not found !is output and the position in the file is not changed. !PAGE ! ! The search is carried out case-independantly. To include the character / in !the search string, it needs to be included twice : F/ABC//DEF/ searches for the !string 'ABC/DEF'. ! F is another command that prints out the position in the file after the !command line has been executed. ! ! !> !< Change Top !T ( to change Top of file ) !--------------------------- ! The first four lines of the file contain parameters for MAILER. These are !IPL discs, Update, This host and Dead Letters. The command T changes these, !exactly like changing a descriptor - replies are *, null or <text>. !> !< Repeat !R ( to repeat a descriptor ) !---------------------------- ! The command R must be followed by the name of a station, in delimiters. !The program takes a copy of the station with the given name, placing it in !front of the current descriptor. !> !< List ! L ( to list file to a character file to be sent to a printer, for example ) !---------------------------------------------------------------------------- ! The command L lists the defaults, followed by the file itself, to a !character file. The name of this file can be specified after the command, !inside delimiters ( "/"'s ). If none is specified, the default name is used !instead. This is the name of the input file, followed by an "L". If the list !file already exists, you are asked whether it is ok to overwrite it. If it !is not ok, the program returns to editing without doing anything. !> !< Compile !K ( to Kompile the file ) !------------------------- ! The parameter for the command K is similar to that for L(ist). If it is !followed by a file name in delimiters, that name is used. Otherwise the default !name ( the input file name suffixed with "Y" ) is used. If a name is specified, !the default file name is set to it too, so if the compilation fails, the name !need only be given once. If the output file so found already exists, you are !asked whether it is ok to overwrite it. If it is, the program tries to compile !the configuration file. If it fails, it returns to the editor. If it succeeds, !the program terminates. !> !< Set up macro !U ( to set up User-defined macro ) !----------------------------------- ! ! U allows you to set up a macro, which can be repeatedly executed. For example, !the macro F/XYZ/PM, followed by the command X10 ( execute macro 10 times ), !will find and print out the next 10 descriptors containing 'XYZ'. ! To set the macro, reply to the prompt ! Macro defn: !with the command(s) you want, just as you would to the prompt ! Config : ! If the command following U is ?, or the reply to the prompt Macro defn: is ?, !the current macro is displayed instead. ! The macro can't contain the commands U, X, A(bort), E(nd) or K(ompile) ! ! !> !< Execute macro !X ( for Execute macro ) !----------------------- ! ! The command X executes the current user-defined macro a certain number of !times - the number following the command ( not zero or negative ), 1 by default. ! The macro cannot contain the commands U, X, A(bort) or E(nd); trying to use !any of these results in the messages ! Can't use U ( or whatever ) in a macro and ! Illegal macro, !the execution of the command line is stopped and the macro is deleted. The same !happens if the macro contains an illegal command ( eg Z, or P0 ), except that !the error messages are ! Illegal command or ! Illegal parameter for PRINT ( or whatever ) !followed by Illegal macro !respectively. If a command like F/ABCD/ fails, the execution of the macro and !the command line ceases, but the macro isn't destroyed. !> !< Abort !A ( for Abort ) !--------------- ! ! The command A aborts the editing session, without changing the input file, if, !for example, you delete all the stations by mistake. ! The program gives you the prompt ! Abort : !to which you reply ! A ( abort ) or ! Y ( yes ) !to abort the editing, and anything else to return to the program without losing !anything. ! ! !> !< End !E ( for End ) !------------- ! ! The command E ends the editing session, saving all the changes made in the !input file. ! !> !> !< Name formats ! The NRS requires that hosts can each be called by several names. The !system used in this program for this is as follows. ! ! a character is either a letter, a digit or a dash "-" ! a word is one or more characters ! an item is a word or null "" ! a list of alternatives is a left bracket "(", ! followed by one or more items separated by commas, ! followed by a right bracket ")" ! a piece is either a word or a list of alternatives ! a component is one or pieces separated by dots ! a name is then one or more components separated by commas ! ! eg "uk.ac.(edinburgh,ed,rco),edinburgh,ed,rco" is legal. ! ! The host so named can then be accessed by any of the alternatives. !In the above example, for instance, there are 6 choices : uk.ac.edinburgh, !uk.ac.ed, uk.ac.rco, edinburgh, ed and rco, with the first of these being !the preferred name. !PAGE ! If any of these 6 alternatives is then included as the first part of another !name, all are considered included. For instance, if the above example were !followed by a host with the name uk.ac.edinburgh.(emas,2972), this host !could be accessed by 12 different names - any of the above 6 followed by !".emas", or any followed by ".2972". Again, it is the first choice that is !the preferred name : uk.ac.edinburgh { the preferred name for the first} !example } .emas ! If an entry is just for a directory rather than a host ( eg uk.ac.edinburgh !rather than uk.ac.edinburgh.emas ) reply BASE to parameter Address type. This !implies that all parameters except Name and Description are irrelevant, so you !won't get prompted for them. !> external routine modftrans(string (31) file) !-------------------------------------------------------------------------------------------------------------------! ! MODFTRANS ! by Jeremy Gibbons ! Version No : 4 ! Creates a spooler configuration file, ready to be analysed. ! Only non-standard ( not default ) parts of a description are included. !-------------------------------------------------------------------------------------------------------------------! record format rf(integer conad, filetype, datastart, dataend) system routine spec change file size(string (31) file, integer newsize, integer name flag) external routine spec copy(string (255) s) external routine spec define(string (255) s) external integer fn spec exist(string (31) file) system routine spec trim(string (31) file, integer name flag) system routine spec connect(string (31) file, integer mode, hole, prot, record (rf) name r, integer name flag) system routine spec outfile(string (31) file, integer filesize, hole, prot, integer name conad, flag) system routine spec disconnect(string (31) file, integer name flag) system routine spec destroy(string (31) file, integer name flag) system routine spec newgen(string (31) file, newfile, integer name flag) system routine spec rename(string (31) file, newfile, integer name flag) system string fn spec failure message(integer return code) system string fn spec itos(integer i) system routine spec move(integer length, from, to) system integer fn spec pack date and time(string (8) date, time) external routine spec prompt(string (15) s) system integer fn spec pstoi(string (63) s) external integer fn spec return code external string fn spec ucstring(string (255) s) external string fn spec uinfs(integer type) !-------------------------------------------------------------------------------------------------------------------! const integer forwards= 1, backwards = -1 const integer already exists= 219 const byte integer delimiter= '/'; ! Delimiter for search string const byte integer ignore= B'00001000', multiple= B'00000100',exit = B'00000010',default = B'00000001' ! Masks for TEMPLATE data const integer ext file header size= 48 const integer n parameters= 15; ! Number of parameters const integer max desc size= 4096; ! Maximum descriptor size ( arbitrary - only used when creating INSERTFILE ) const integer no of commands= 16 const integer read and write= 3; ! Mode to connect files in const integer ok= 0, not ok = 1 constbyteinteger file service = x'01' constbyteinteger mail service = x'02' const integer worksize=128000 const integer hash length=1023; !must be 2**n -1 const integer max fsys=99 const integer update flag=1 const integer update copy flag=2 const integer route flag=4 const integer this auth flag=8 const integer this host flag=16 constinteger local host flag = 32 const integer found=1, not found = 0, yes = 1, no = 0 constinteger closed = no const string (1) snl= " "; ! Newline string const string (11) workfile= "CONFIG#WORK"; ! Name of file all work is done in const string (11) insertfile= "CONFIG#NEW"; ! Name of file where new descriptor is built up const string name date= X'80C0003F', time = X'80C0004B' const byte integer array command(1:no of commands)= c '?','P','M','F','I','D','C','U','X','!','A','E','K','L','R','T' ! All the legal commands const byte integer array template(1:2, 1:n parameters)= c { NSI } { TS } B'00000000', B'00000000', B'00000000', B'00000000', B'00000001', B'00000001', B'00000001', B'00000001', B'00000000', B'00110100', B'00000000', B'00000000', B'00000011', B'00000011', B'00000011', B'00001000', B'00001000', B'00000011', B'00001000', B'00000011', B'00000011', B'00000011', B'00000011', B'00000011', B'00000011', B'00000011', B'00000011', B'00000011', B'00000011', B'00000011' ! Template B'abcdefgh' ! ! abcd : Maximum no of multiple inputs - 1, if allowed ( ignored if not ) - ie B'0000' means max 1 ! ( so it is possible to represent 16 in 4 bits, since 0 is redundant ) ! ! e = 1 : Ignore this parameter ( it has to be NA ) ! ! f = 0 : Only single input ! f = 1 : Multiple inputs allowed ! ! g = 0 : Can't exit from here ( this or later parms have no default ) ! g = 1 : Can exit from here ( this and later parms all have defaults ) ! ! h = 0 : No default for this parm ! h = 1 : This parm has a default const integer max stations= 512; ! Max. number of stations const string (11) array parameter names(1:n parameters)= c "Name","Description","Addr type","Short name","Address","FEP","Services", "PSS No","FTP","Mail Suffix","Limit","Lines","Status", "M route", "M update" const string (255) array descriptor defaults(1:n parameters)= c "","","TS","chosen by the Kompiler.","","","FILE&MAIL","NA","FTP","MAIL","5000","1","0", "NA", "No" const string (255) array help info(1:n parameters)= c "Name of this station", "Description of station", "TS or NSI, or BASE if descriptor is just for a directory", "Shortest acceptable name, if different from logical one", "Station network address or PSS network base ( .END to stop adding addresses )", "The front end we wish ( for the time being ) to confine the activity to", "What services are available at the Station (ie FILE MAIL )", "PSS table entry if relevant", "The FTP 'called' field for the station ( TS only )", "The Mail 'called' field extender ( TS only )", "Default limit", "Default lines", "Station status ( 0 is full service, 1 is test station )", "Route to host", "Whether to send directory info to host ( reply No, Yes or All )" ! Help info for each parameter string (6) owner string (11) output file, default output file, list file, default list file string (20) this full host string (31) filename string (255) find1, find string, line, line1, macro string, save string, dummy, dummy1 integer address, conad, count, current position, flag, flag1, flag2, i, j, len, pdesc, sign, temp cp, value, work conad, macro, addr type, base type byte integer name l integer start position, end position record format efhf(integer end, start, size, type, sp1, datetime, sp2, sp3, checkword, stations, sp4, sp5) ! EXT File header format record (rf) r record (efhf) name ext file header !-------------------------------------------------------------------------------------------------------------------! byte integer fn print out(integer type, string (255) line, integer address) ! Copies the string LINE to destination which is ! ! CONAD + ADDRESS if TYPE = 0 ! ! WORK CONAD + ADDRESS otherwise ! ! Returns length of LINE as result. ! Sets FLAG = 1 if LINE won't fit into the insertfile and TYPE isn't 0 integer i, conad1 if type=0 then conad1 = conad else conad1 = work conad; ! Set absolute address if address+length(line)>max desc size and type#0 then flag = 1 else start ! Check if LINE is too long - only if copying LINE to insertfile: LINE can't be too long otherwise flag = 0 byteinteger(conad1+address+i-1) = charno(line, i) for i = 1, 1, length(line) ! Copy line finish result = length(line) end ; ! of byteintegerfn PRINT OUT !-------------------------------------------------------------------------------------------------------------------! routine change no of devices ! Changes the first line ( the one that reads "Stations = 1 " or whatever ) to ! reflect the number of stations integer dummy string (3) temp temp = itos(ext file header_stations); ! Convert to a string temp = temp." " while length(temp)<3; ! Pad out to 3 characters in length dummy = print out(0, temp, start position-5); ! Output to file ! START POSITION points to here ___ ! | ! _________________| ! | ! V ! Stations = 1 **STATION = ... ( where * represents a NEWLINE character ) ! A ! |___________________________________ ! | ! so START POSITION - 5 points to here _________| end ; ! of routine CHANGE NO OF DEVICES !--------------------------------------------------------------------------------------------------------------! routine change end position(integer by) ! Moves END POSITIOn and EXT FILE HEADER_END up or down by BY. end position = end position+by ext file header_end = ext file header_end+by end ; ! of routine CHANGE END POSITION !-------------------------------------------------------------------------------------------------------------------! string fn padout(string (255) s, byte integer l) s = s." " while length(s)<l result = s end { of padout } !--------------------------------------------------------------------------------------------------------------! byte integer fn upper(integer ch) ! Result is CH, unless CH is the code for a lower case character, in which case the result is ! the code for the equivalent upper case character. if 'a'<=ch<='z' then result = ch-32 else result = ch end ; ! of byteintegerfn UPPER !-------------------------------------------------------------------------------------------------------------------! routine print desc(integer address) ! Prints out a descriptor starting at CONAD + ADDRESS, and finishing on the next double newline ! ( which is also printed out ). ! Also informs the user if this is the first and/or the last descriptor . integer i, ch, last ch printstring("** First station **".snl.snl) if address=start position ! Inform user if this the first descriptor of a type. address = conad+address; ! Change ADDRESS to absolute, instead of relative to the start of the file. if byteinteger(address)=nl and byteinteger(address+1)=nl then c printstring("** Last station **".snl.snl) else start ! Inform user if this is the last station. If not, print it out. i = 0; ! Pointer ch = ' '; ! Simulate 'last character' cycle last ch = ch ch = byteinteger(address+i); ! Get next character i = i+1 printsymbol(ch) repeat until (ch=nl and last ch=nl) or address+i>=conad+ext file header_end ! Exit after printing a double newline, or off end of file finish end ; ! of routine PRINT DESC !-------------------------------------------------------------------------------------------------------------------! integer fn locate(integer direction, start, end, string (255) text) ! Searches for an occurrence of TEXT totally within the range START to END - 1, independant of case. ! If DIRECTION = 1, the search is conducted forwards ( finding the first occurrence ), ! if DIRECTION = -1, the search is conducted backwards ( finding the last occurrence ). ! If the length of TEXT is 0, a result of START is returned if DIRECTION = 1, or END if DIRECTION = -1. ! If END - START < length of TEXT, or DIRECTION isn't 1 or -1, or no match is found, the result ! returned is zero, otherwise the result is the ( absolute ) address of the first byte of the image. integer len, address, i len = length(text); ! Length of pattern address = addr(text)+1; ! Start of pattern end = end-len; ! From now on END represents the first byte of the last possible match of TEXT in the range. i = 0; ! Pointer to which character of TEXT is being checked - 0 means the first, LEN-1 means the last. if direction=1 start ; ! Forwards search while i<len and start<=end cycle ; ! Exit if off end of TEXT ( match found ) or range ( no match ). i = i+1 while upper(byteinteger(start+i))=byteinteger(address+i) and i<len ! Try to match TEXT - cycle through TEXT until a character doesn't fit, or there are no more to try. start = start+1 and i = 0 if i<len; ! I < LEN implies no match found, so reset I ( search for first ! character again ) and increment START ( for next address ). repeat if i<len then result = 0 else result = start; ! I < LEN implies no match found ( result = 0 ), ! otherwise the result is the start of the image. finish else start ; ! Backwards search result = 0 unless direction=-1; ! Check DIRECTION was legal. while i<len and start<=end cycle ; ! as above i = i+1 while upper(byteinteger(end+i))=byteinteger(address+i) and i<len ! Match starts at END of range instead of START. end = end-1 and i = 0 if i<len; ! Reset I and decrement END ( for next try ) if no match found. repeat if i<len then result = 0 else result = end finish end ; ! of integerfn LOCATE !-------------------------------------------------------------------------------------------------------------------! byte integer fn ch ! Result is code of last character of LINE, unless length of LINE is 0, in which case the result is 0. if l=0 then result = 0 else result = charno(line, l) end ; ! of byteintegerfn CH !-------------------------------------------------------------------------------------------------------------------! routine read value ! Reads a number off the end of LINE. If the first character ( last character of LINE ) is a '-', SIGN is ! set to -1, otherwise SIGN is set to 1. If the next character is '*' ( meaning "as much as possible" ), ! VALUE is set to the maximum number of devices for the current type. If the next character ! is non-numeric, a default of 1 is assumed, otherwise an integer value ( up to 4 digits ) is ! read off the end of LINE into VALUE. string (4) temp if ch='-' then l = l-1 and sign = -1 else sign = 1; ! Sign of value if ch='*' then l = l-1 and value = max stations else start temp = "" while l>0 and '0'<=ch<='9' and length(temp)<5 cycle ; ! Exit if off the end ! of LINE, next character isn't a digit, or TEMP is too long. temp = temp.tostring(ch); ! Add character to TEMP. l = l-1; ! Decrement length of LINE repeat temp = "1" if temp=""; ! Default is 1 value = pstoi(temp); ! Convert to integer finish end ; ! of routine READ VALUE !-------------------------------------------------------------------------------------------------------------------! routine read command(string (255) name s) ! Reads a line from the terminal, and stores it back to front in S ( this makes it easier to change the ! line after executing one command : all that is needed is L = L - 1 instead of ! LINE = SUBSTRING ( LINE, 2, L - 1 ) ! Ignores spaces unless inside delimiters ("/"). ! Line stops at NEWLINE ( and doesn't include it ) or when its length is 255. ! All characters are converted to upper case, even those in delimiters since any search is case independant. ! If a line ends after an odd number of delimiters, it is rejected and another is entered. ! NB 2 delimiters in a row ( ...//... ) would be included as one delimiter in the search string, ! and also this doesn't change the parity of the number of delimiters, so only the parity of ! DELIMIT needs to be checked. integer symbol, delimit start: s = "" delimit = 0; ! No delimiters yet cycle readsymbol(symbol) exit if symbol=nl; ! Newline terminates command. delimit = delimit!!1 if symbol=delimiter; ! Change parity of DELIMIT if a delimiter is found. continue if symbol=' ' and delimit=0; ! Ignore a space unless inside delimiters. s = tostring(upper(symbol)).s; ! Add to beginning of line. repeat until l=255; ! Exit if S maximum length. printstring("Must be an even number of delimiters ( / )".snl) and ->start unless delimit=0 ! S must have an even number of delimiters. end ; ! of routine READ COMMAND !-------------------------------------------------------------------------------------------------------------------! routine read line ! Inputs LINE1 from terminal, ignoring any spaces. ! LINE1 is terminated by a newline ( which isn't included ) or when its length is 200. integer ch line1 = "" cycle readsymbol(ch) exit if ch=nl line1 = line1.tostring(ch); ! Add to line repeat until length(line1)=200 end ; ! of routine READ LINE !--------------------------------------------------------------------------------------------------------------! integer fn parm(integer param no, string (255) in, string (*) name out) ! Sets string OUT to a valid parameter to be put into file. IN is the response typed to a prompt ! ( eg Name: ), PARAM NO is the number of the parameter ( 1-13 ) out = ucstring(parameter names(param no))." = ".in.snl result = length(out) end ; ! of integerfn PARM !--------------------------------------------------------------------------------------------------------------! string fn get a param(integer param no, integer name flag) ! Gets a parameter input from the terminal. Prints out the default for the parameter if there is one, ! then prompts with the name of the parameter. If the response is ?, prints out some help info ! then asks for another input. If parameter can take multiple inputs ( eg Address: ) ! inputs several parameters and concatenates them, till the response ! .END is reached, or 16 inputs have been entered. If a null line is entered, and this ! parameter has a default, the result is null and FLAG is set to 1. If a * is entered, and this ! and all the following parameters have defaults, the result is null and FLAG is set to 2 ! ( leave rest of descriptor as defaulted ). If a null line or * is entered, and these conditions ! aren't satisfied, another input is requested. If neither of these has been input, the line is ! PARMed ( with fn PARM ). ! If .END is entered when multiple inputs aren't allowed, the line is taken as a null line. string (255) dummy integer limit string (*) fn no spaces(string (255) s) string (255) one, two s = one.two while s->one.(" ").two result = s end { of NO SPACES } dummy = "" flag = 0 limit = 1+template(addr type, param no)>>4; ! Max no of multiple inputs if allowed prompt(parameter names(param no).":") count = 0; ! for multiple inputs cycle printstring("Default for ".parameter names(param no)." is ".descriptor defaults(param no).snl) unless c template(addr type, param no)&default=0 ! Print default if there is one read line if line1="?" start ; ! Help info printstring(help info(param no).snl) continue ; ! Get more input finish if ucstring(line1)=".END" start if count=0 then line1 = "" else exit ! Simulate "accept default" if this is first input finish printstring(parameter names(param no)." does not have a default".snl) and continue if c (line1="" or line1="*") and template(addr type, param no)&default=0 ! User tried to use default when there wasn't one. printstring("More obligatory parameters to come".snl) and continue if c line1="*" and template(addr type, param no)&exit=0 ! User tried to exit from this descriptor when there were more obligatory descriptors to come if line1="" then flag = 1 and exit if line1="*" then flag = 2 and exit printstring("Parameter too long".snl) and continue if length(dummy)+parm(param no, line1, dummy1)>255 ! Check parameter won't overflow ( especially with multiple inputs ) if param no=3 start if ucstring(no spaces(line1))="NSI" then addr type = 1 else if c ucstring(nospaces(line1))="BASE" then base type = yes finish dummy = dummy.dummy1; ! DUMMY1 set by PARM exit if template(addr type, param no)&multiple=0 or count=limit-1; ! if only single input allowed ! or if maximum number of inputs reached. count = count+1; ! Increment number of inputs so far repeat result = dummy end ; ! of stringfn GET A PARAM !------------------------------------------------------------------------------------------------------------! integer fn compile config(string (11) output file) !*********************************************************************** !* * !* Purpose of this routine is to read in a spooler configuration file * !* which is in text format and create a file for passing on to the * !* spooler process as a data base. If result = 0 then the read was * !* successful, if not it wasn't. * !* * !*********************************************************************** const string (0) null= "" const integer max lines= 25 const integer pointer size= 19*4 + max fsys+1 + 2*64 + 4 + 4*(1+hash length) ! Size of POINTERS record const integer file header size= 32 const integer read permission= 1 const integer link list entries= 1000 const integer link list entry size= 32 const integer displacement= file header size + pointer size + link list entries *link list entry size ! Displacement of Stations from start of file const integer stream entry size= 192; !Number of bytes in a stream descriptor const integer queue entry size= 148 const integer station entry size= 512; ! Number of bytes in a station descriptor const integer string space size= 376 const integer expanded addresses size= 20 const integer ftp table entry size= 618 const string (11) temp output file= "T#CFILE"; ! Name of temporary file created const string (15) array stnd(1:n parameters)= c "NAME=", "DESCRIPTION=", "ADDRTYPE=", "SHORTNAME=", "ADDRESS=", "FEP=", "SERVICES=", "PSSNO=", "FTP=", "MAILSUFFIX=", "LIMIT=", "LINES=", "STATUS=", "MROUTE=", "MUPDATE=" const byte integer array stndt(1:n parameters)= c 255, 255, 4, 15, 255, 0, 255, 0, 255, 255, 0, 0, 0, 255, 3 const string (27) mr legal chars 1= "ABCDEFGHIJKLMNOPQRSTUVWXYZ*" const string (40) mr legal chars 2= "ABCDEFGHIJKLMNOPQRSTUVWXYZ*0123456789-.%" record format pointers f(integer link list displ, ftp table displ, queues, queue entry size, queue displ, queue name displ, streams, stream entry size, stream displ, hash len, spare1, spare2, spare3, stations, station entry size, station displ, control entry, station addresses displ, guest entry, byte integer array discs(0:max fsys), string (63) dead letters, this full host, integer expanded address displ, integer array hash t(0:hash length)) ! Format of pointers record at start of file record format fhf(integer end, start, size, type, spare, datetime, half integer queues, remotes, streams, stations) record format pe(integer dest, srce, p1, p2, p3, p4, p5, p6) ! recordformat line f(string (15) name, string (7) unit name, string (6) user, byteinteger parity, integer status, bytes sent, bytes to go, block, part blocks, document, integer bin offset, byteinteger service, user abort, unit size, fep, integer abort retry count, offset, station ptr, integerarray ispare(0:2), integer data transfer start {for timing the transfer},account, integer in comms stream, out comms stream, integer in stream ident, out stream ident, integer transfer status, tcc subtype, in block addr, out block addr, byteinteger activity, station type, spb2, suspend, in stream status, out stream status, timer, output buffer status, output transfer pending, new FTP data record, byteintegerarray bspare(0:9), integer aux document, pre abort status, bytes transferred, record (pe) output transfer record) !* !* !* !* record format queuef(string (15) name, (half integer array ftp lines(0:15) or c half integer array lines(0:15)), string (7) default user, string (31) default delivery, integer default start, default priority, default time, default output limit, default forms, default mode, default copies, default rerun, length, head, max length, maxacr, half integer q by, general access, integer resource limit, amount) record format station f(byte integer max lines , byte integer status, byte integer service , byte integer connect retry ptr, fep, address type, services, byte integer q lines , integer limit , integer last call, last response, system loaded, connect attempts, connect retry time, integer array ispare(0:4), integer seconds, bytes, integer last q response by us, p transfers, q transfers, p kb, q kb, p mail, q mail, integer name, shortest name, integer array address(1:4), integer pss entry, mail, ftp, description, route, integer flags, byte integer array string space(0:string space size-1)) record format compf(integer link, host entry, alt, string (63) c) record format name f(integer link, host entry, string (255) name) record format exp addr f(integer type, integer array ad(1:4)) byte integer array x(1:worksize) record (compf) name comp record (name f) name name entry string (63) this authority, this host integer freetop, top level, alt link, end comp integer worktop, size, n pt system routine spec permit(string (31) file, string (6) user, integer mode, integer name flag) record (exp addr f) array format exp addr af(1:max stations) record (exp addr f) array name expanded address record (fhf) name file header record (line f) name stream entry record (queue f) name queue entry record (station f) name station entry record (line f) default stream entry record (station f) default station entry record (pointers f) name pointers string (255) line, temps, temp1, temp2 own string (255) ns1 integer text pointer, text end, flag, i, j, k, value, o f conad, config size, stations, result, address pt, string pt, default string pt switch stnswt(1:n parameters) !--------------------------------------------------------------------------------------------------------------! routine ftf(string (255) s) printstring("Failed to find ".s.snl) return end { of ftf } !--------------------------------------------------------------------------------------------------------------! string fn station string(integer d) result = string(addr(station entry_string space(d))) { empty strings mapped onto first byte of space which is zero, so = "" } end { of station string } !--------------------------------------------------------------------------------------------------------------! integer fn set string(string (255) s) integer res res = string pt string(addr(station entry_string space(string pt))) = s string pt = string pt+1+length(s) if string pt>string space size then printstring("String space exceeded".snl) and result = 1 result = res end { of set string } !--------------------------------------------------------------------------------------------------------------! routine read line(integer parm no) !*********************************************************************** !* * !* Reads a line of text terminated by a newline. Returns a newline * !* if first character is newline ( blank line ). Skips leading * !* newlines and spaces after this ( any number of blank lines is * !* interpreted as just one ). Ignores spaces except in comments. * !* Exits if text pointer = text end ( end of file ). Returns a * !* null line if at end of file. Ignores rest of line if length > * !* 255 characters. FLAG must be 1 on entry ( 'last line has been * !* used' ). Sets FLAG to 0 ( 'line not yet used' ). * !* * !*********************************************************************** integer sym, uc return if flag=0; ! If last line not yet used uc = 0 line = null flag = 0; ! This line not yet used line = snl if byteinteger(text pointer)=nl and text pointer<text end; ! Return SNL ! if this is a blank line ( to distinguish against 'end of file' ) text pointer = text pointer+1 while c text pointer<text end and (byteinteger(text pointer)=' ' or byteinteger(text pointer)=nl) ! Skip leading newlines and spaces return if text pointer=text end or line=snl; ! If end of file reached, or this line is blank sym = byteinteger(text pointer); !Read a symbol text pointer = text pointer+1; ! And skip over it while sym#nl and text pointer<text end cycle ; ! until end of line or file if sym='=' and parm no=2 then uc = 1 { ie in DESCRIPTION parameter, and after '=' } sym = sym-32 if 'a'<=sym<='z' and uc<2; ! Convert to upper case line = line.to string(sym) unless (sym=' ' and uc<2) or length(line)=255 ! No spaces except within description . Make sure line doesn't overflow if sym=' ' and uc=1 then uc = 2 sym = byteinteger(text pointer); ! Get a symbol text pointer = text pointer+1; ! and skip over it repeat end ; ! Of routine READ LINE !--------------------------------------------------------------------------------------------------------------! string fn expand address(string (127) original) string (127) new string, workstring integer fn all numeric(string (127) s) integer i cycle i = 1, 1, length(s) result = no unless '0'<=charno(s, i)<='9' repeat result = yes end newstring = "" cycle exit if original="" unless original->workstring.(".").original then workstring = original and original = "" if newstring#"" then newstring = newstring."." newstring = newstring.workstring and continue if all numeric(workstring)=no if length(workstring)=12 then newstring = newstring.workstring."00" and continue if length(workstring)<12 start workstring = "0".workstring while length(workstring)#12 workstring = workstring."00" finish newstring = newstring.workstring repeat result = newstring end !--------------------------------------------------------------------------------------------------------------! string fn shortest name(integer host entry) integer min comps, i, j, link record (name f) name name entry string (63) sh name string (63) array component(1:20, 1:2) integer fn n comps(string (63) name) integer i string (63) s1, s2 i = 1 i = i+1 and name = s1.s2 while name->s1.(".").s2 result = i end { of n comps } routine get comps(string (63) name, integer dim) integer i cycle i = 1, 1, 20 exit if name="" unless name->component(i, dim).(".").name start component(i, dim) = name name = "" finish repeat end { of get comps } min comps = 21 sh name = "" cycle i = 0, 1, hash length link = pointers_hash t(i) while link#-1 cycle name entry == record(o f conad+link) if name entry_host entry=host entry start if n comps(name entry_name)<min comps start sh name = name entry_name min comps = n comps(sh name) get comps(sh name, 1) finish else if n comps(name entry_name)=min comps start get comps(name entry_name, 2) cycle j = 1, 1, min comps if length(component(j, 2))>length(component(j, 1)) then exit if length(component(j, 2))<length(component(j, 1)) start sh name = name entry_name get comps(sh name, 1) exit finish repeat finish finish link = name entry_link repeat repeat result = sh name end { of shortest name } !--------------------------------------------------------------------------------------------------------------! routine fail(string (255) s) printstring("Fails - ".s.snl) result = 1 end ; !of fail integer fn new comp rec record (compf) name comp integer ad ad = freetop freetop = freetop+76 if freetop>worktop then fail("Work space exceeded") and result = top level comp == record(ad) comp = 0 result = ad end { of new comp rec } integer fn hashed(string (63) name) integer i, pt, n, h byte integer array x(0:15) const byte integer array prime(1:7)= 23, 19, 11, 7, 5, 13, 17 pt = (addr(x(7))>>3)<<3 longinteger(pt) = 0 n = addr(name) byteinteger(pt+i&7) = byteinteger(pt+i&7)!!byteinteger(n+i) for i = 0, 1, length(name) h = length(name)*29 h = h+prime(i)*byteinteger(pt+i) for i = 1, 1, 7 result = h&hashlength end ; !of hashed routine add hash(integer host entry, string (63) name) integer ad, h name -> (".").name if charno(name,length(name))='.' then length(name) = length(name)-1 h = hashed(name) ad = n pt n pt = (n pt+8+1+length(name)+3)&(¬3) name entry == record(ad) name entry_link = pointers_hash t(h) pointers_hash t(h) = ad-o f conad name entry_host entry = host entry name entry_name = name {***}printstring(name." Host=".itos(host entry).snl) end { of add hash } routine generate names(integer link, string (63) h) record (compf) name comp if link#0 start comp == record(link) while comp_alt#0 cycle if comp_host entry#0 then add hash(comp_host entry, h.".".comp_c) generate names(comp_link, h.".".comp_c) comp == record(comp_alt) repeat finish end { of generate names } integer fn process name(string (255) hname, integer host entry) integer type, pt, end, act string (63) str, extra, c, pref name byte integer array t(1:20) string (63) array tv(1:20) integer tokens, tok switch sw(1:9) const integer single comp=3 const integer alt start=4 const integer next alt=7 const integer alt end=6 const integer error=8 const integer part complete=5 const integer all complete=2 integer fn last component integer i for i = tok+1, 1, tokens cycle if t(i)=single comp or t(i)=alt start then result = no if t(i)=part complete or t(i)=all complete then result = yes repeat result = 0 end ; !of last component routine add name(string (63) c) comp == record(comp_alt) while comp_alt#0 comp_c = c comp_alt = new comp rec if last component=yes then comp_host entry = host entry end { of add name } integer fn search(string (63) c) cycle if comp_c=c then result = found if comp_alt=0 then result = not found comp == record(comp_alt) repeat end { of search } integer fn next token integer cl, char switch ac(0:9) const byte integer array class(0:127)= c 7(32), 5, 7(7), 2, 3, 7(2), 4, 1, 6, 7, 1(10), 7(7), 0(26), 7(37) const byte integer array actionstate(0:8, 0:5)= c 16_11, 16_11, 16_42, 16_8f, 16_8f, 16_00, 16_8f, 16_8f, 16_20, {scanning} 16_11, 16_11, 16_8f, 16_8f, 16_35, 16_01, 16_30, 16_8f, 16_30, {building name} 16_13, 16_13, 16_8f, 16_74, 16_72, 16_02, 16_8f, 16_8f, 16_8f, {scanning alt} 16_13, 16_13, 16_8f, 16_74, 16_72, 16_03, 16_8f, 16_8f, 16_8f, {building alt} 16_8f, 16_8f, 16_8f, 16_8f, 16_65, 16_04, 16_60, 16_8f, 16_60, {end alt} 16_90, 16_90, 16_90, 16_90, 16_90, 16_90, 16_90, 16_90, 16_90 {after end alt} ! letter dig- ( ) , sp . rest end ! elements 1,0 & 1,2 changed from 8f to allow numerics (like 2972) meantime str = "" cycle pt = pt+1 if pt>end then cl = 8 else char = byteinteger(pt)&255 and cl = class(char) act = actionstate(cl, act&15) ->ac(act>>4) ac(0): !do nothing continue ac(1): !start building name str = str.tostring(char) continue ac(2): !return end of line ac(3): !return name ac(4): !return construct ac(5): !return end of name ac(6): !end of alternatives ac(7): !return alternative ac(8): !error result = act>>4 ac(9): !rescan char pt = pt-1 result = 5 repeat end ; !of next token cycle extra = "" type = 0; act = 0 pt = addr(hname) end = pt+length(hname) tokens = 0 cycle tokens = tokens+1 t(tokens) = next token tv(tokens) = str if t(tokens)=error or t(tokens)=all complete then exit repeat tok = 0 end comp = 0 cycle pref name = "" comp == record(top level) cycle tok = tok+1 c = tv(tok) ->sw(t(tok)) sw(single comp): pref name = pref name.".".c if search(c)=found start if last component=yes then fail("Duplicate name ".pref name) and result = not ok comp == record(comp_link) finish else start add name(c) if last component=yes and 0#endcomp#addr(comp) then comp_link = end comp c else comp_link = new comp rec comp == record(comp_link) finish continue sw(alt start): comp == record(comp_alt) while comp_alt#0 alt link = new comp rec if tv(tok+1)#"" then pref name = pref name.".".tv(tok+1) continue sw(next alt): add name(c) comp_link = alt link comp == record(comp_alt) continue sw(alt end): comp == record(alt link) continue sw(error): fail("Parsing error in ".hname) result = not ok sw(part complete): end comp = addr(comp) sw(all complete): if station entry_name=0 start station entry_name = set string(substring(pref name, 2, length(pref name))) finish exit repeat if pref name->(this authority.".").hname and hname#"" start if extra#"" then extra = extra."," extra = extra.hname finish if t(tok)=all complete then exit repeat if extra="" then exit hname = extra repeat result = ok end ; !of process name routine set flags(string (255) name, integer flag) integer i integer fn lookup hasht(string (63) name) record (name f) name name entry integer h h = hashed(name) if pointers_hasht(h)#-1 start name entry == record(of conad+pointers_hasht(h)) cycle if name=name entry_name then result = name entry_host entry exit if name entry_link=-1 name entry == record(of conad+name entry_link) repeat finish result = 0 end ; !of lookup hasht i = lookup hasht(name) if i=0 then fail("No match for auth/host ".name) else start station entry == record(of conad+pointers_station displ+(i-1)*station entry size) station entry_flags = station entry_flags!flag!local host flag finish end ; !of set flags !--------------------------------------------------------------------------------------------------------------! result = 2; ! Not successful ( or unsuccessful ) as yet stations = 0 default stream entry = 0; ! Clear to zeroes station entry == default station entry string pt = 1 station entry = 0; ! Clear to zeroes station entry_address type = 2 { TS }; ! Defaults for stations station entry_services = file service ! mail service station entry_ftp = set string("FTP") station entry_mail = set string("MAIL") station entry_limit = 5000 station entry_last response = -1 station entry_last q response by us = -1 station entry_max lines = 1 default string pt = string pt text pointer = conad+ext file header size; !Addr of first byte (used by read line) text end = conad+end position; !Addr of last byte + 1 (used by read line) config size = displacement+queue entry size+max lines*stream entry size+max stations* c (station entry size+expanded addresses size)+(max lines+1)*ftp table entry size+128000 { for names of stations from hash table } ! Max possible size if exist(temp output file)#0 start disconnect(temp output file, flag) destroy(temp output file, flag) finish else flag = 0 if flag=0 start outfile(temp output file, config size, 0, 0, o f conad, flag) if flag=0 start ; !Successfully created config file freetop = (addr(x(1))+3)&(¬3) worktop = freetop+work size top level = new comp rec this host = this full host size = 0 cycle i = 1, 1, length(this host) if charno(this host, i)='.' start size = size+1 if size=3 then this authority = substring(this host, 1, i-1) and exit finish repeat if size#3 then fail("Bad value for this host") file header == record(o f conad); ! Map file header pointers == record(o f conad+file header size); ! and POINTERS record pointers_link list displ = file header size+pointer size pointers_hash len = hash length flag = 1 and read line(0) result = 1 and ftf("IPL discs") and ->error unless line->("IPLDISCS=").temps pointers_discs(i) = 0 for i = 0, 1, max fsys while temps#null cycle temp1 = temps and temps = null unless temps->temp1.(",").temps j = pstoi(temp1) result = 1 and printstring("Illegal IPL disc ".temp1.snl) and ->error unless 0<=j<=max fsys pointers_discs(j) = 1 repeat flag = 1 and read line(0) if line->("UPDATE=").temps start while temps#null cycle temp1 = temps and temps = null unless temps->temp1.(",").temps j = pstoi(temp1) if 0<=j<=max fsys then pointers_discs(j) = pointers_discs(j)!2 else c result = 1 and printstring("Illegal disc for update : ".temp1.snl) and ->error repeat finish else ftf("update") and ->error flag = 1 and read line(0) if line->("THISHOST=").temps then pointers_this full host <- temps else ftf("This host") and ->error flag = 1 and read line(0) if line->("DEADLETTERS=").temps then pointers_dead letters <- temps else c ftf("Dead letters") and ->error !MAKE THE QUEUE SPACE. pointers_queue displ = displacement queue entry == record(o f conad+pointers_queue displ) queue entry_default user = "FTRANS" queue entry_default delivery = "SPOOLED File Transfer" queue entry_default start = 32 queue entry_default priority = 3 queue entry_max length = 1000 queue entry_max acr = 15 queue entry_resource limit = 1000 !* stream input section pointers_streams = max lines pointers_stream displ = pointers_queue displ+queue entry size pointers_stream entry size = stream entry size cycle i = 1, 1, max lines; ! Round each stream stream entry == record(o f conad+pointers_stream displ+(i-1)*stream entry size) ! Map stream entry stream entry = default stream entry; ! Set defaults stream entry_name = "FT".itos(i) repeat !* Station input section flag = 1 and read line(0) until line->ns1.("STATIONS=").temps or line=null ! Read lines til no of stations found or end of file reached flag = 1; ! This line now used if line#null and ns1=null start ; ! Is it no of stations ? temps = temp1 if temps->temp1.("!").temp2; ! Remove comments stations = pstoi(temps) if 1<=stations<=max stations start ; ! Valid no of stations ? pointers_stations = stations+2 { for guest and operational control } pointers_station displ = (pointers_stream displ+max lines*stream entry size+511)&(~(511)) pointers_expanded address displ = pointers_station displ+pointers_stations*station entry size pointers_station addresses displ = pointers_expanded address displ+pointers_stations*expanded addr c esses size ! Set POINTERS entries pointers_station entry size = station entry size address pt = 1 expanded address == array(o f conad+pointers_expanded address displ, exp addr af) cycle i = 1, 1, stations; ! Round each station string pt = default string pt station entry == record(o f conad+pointers_station displ+(i-1)*station entry size) expanded address(i)_type = station entry_address type { default } ! Map station entry station entry = default station entry; ! Set defaults cycle j = 1, 1, n parameters; ! For each parameter read line(j) flag = 1 and read line(j) while j=1 and line=snl; ! Remove blanks before descriptor flag = 1 and exit if line=null or line=snl; ! End of file or this descriptor ? if line->ns1.(stnd(j)).temps and ns1=null start ; ! For this param ? temp2 = null unless temps->temps.("!").temp2; ! Remove comments ->nextstn if temps="NA" or temps="NOTAPPLICABLE"; ! Ignore if NA if stndt(j)=0 start ; ! Numeric value = pstoi(temps) printstring("Invalid parameter".snl) and ->failstn if value<0 finish else start printstring("String wrong size".snl) and ->failstn unless 1<=length(temps)<=stndt(j) finish ->stnswt(j) stnswt(1): ! Name of station ->failstn unless process name(temps, i)=ok ->nextstn stnswt(2): ! The description of the station. if temps->("!").temp2 then temps = temp2 station entry_description = set string(temps) ->nextstn stnswt(3): ! The type of addressing for this FTP station, ie TS or NSI access ! If temps is "BASE", this descriptor is not a host, but a directory if temps="BASE" then station entry_status = 9 C and station entry_services = 0 and station entry_address type = 3 else c if temps="TS" then station entry_address type = 2 else station entry_address type = 1 expanded address(i)_type = station entry_address type ->nextstn stnswt(4): ! Shortest name station entry_shortest name = set string(temps) ->nextstn stnswt(5): ! Station network address or PSS network base cycle cycle k = 1, 1, 4; ! Try to find an empty member of ADDRESS if station entry_address(k)=0 then ->stn out ! Found a space repeat ; ! If this comment is reached, ADDRESS array is full printstring("Too many address ( 4 max )".snl) ->failstn stn out: station entry_address(k) = address pt string(o f conad+pointers_station addresses displ+address pt) = temps address pt = address pt+1+length(temps) temps = expand address(temps) expanded address(i)_ad(k) = address pt string(o f conad+pointers_station addresses displ+address pt) = temps address pt = address pt+1+length(temps) ! Check for multiple addresses flag = 1 read line(j); ! Get next line exit unless line->ns1.(stnd(j)).temps and ns1=null ! ie unless there is another address temps = temp1 if temps->temp1.("!").temp2; ! Remove comments repeat ; ! Now go back to STN SWT ( 5 ) to do next address exit if line=null or line=snl; ! ie if out of this station or of file ->nextstn1; ! Don't set FLAG to 1, since next line has already been read stnswt(6): ! The front end we wish ( for the time being ) to confine the activity to station entry_fep = value ->nextstn stnswt(7): !The services offered by the external station. station entry_services = 0 if temps -> temp1.("FILE").temp2 then station entry_services = file service if temps -> temp1.("MAIL").temp2 then station entry_services = c station entry_services ! mail service ->nextstn stnswt(8): ! The PSS table entry if relevant station entry_pss entry = value ->nextstn stnswt(9): ! The FTP 'called' field for the station ( TS only ) station entry_ftp = set string(temps) unless station entry_address type=1 ->nextstn stnswt(10): ! The MAIL 'called' field extender ( TS only ) station entry_mail = set string(temps) unless station entry_address type=1 ->nextstn stnswt(11): ! Default limit station entry_limit = value ->nextstn stnswt(12): ! Default lines if value>255 then printstring("Value must be <= 255".snl) and ->failstn else c station entry_max lines = value ->nextstn stnswt(13): ! The station status ( 0 is full service, 1 is test station ) station entry_status = value ->nextstn stnswt(14): ! Route ->failstn unless mr legal chars 1->(substring(temps, 1, 1)) cycle k = 2, 1, length(temps) ->failstn unless mr legal chars 2->(substring(temps, k, k)) repeat station entry_route = set string(temps) station entry_flags = station entry_flags!route flag ->nextstn stnswt(15): ! update if temps="YES" then station entry_flags = station entry_flags!update flag else if c temps="ALL" then station entry_flags = station entry_flags!update copy flag else c unless temps="NO" then ->failstn ->nextstn finish continue unless j=n parameters; ! Not for this parm. Try next if any left failstn: result = 1; ! Definitely unsuccessful printstring("Descriptor ".itos(i)." ".station string(station entry_name)." Parameter ".stnd c (j)." wrong : ".line.snl) nextstn: flag = 1; ! This line now dealt with nextstn1: repeat ; ! Each parameter exit if line=null; ! End of file repeat ; ! Each descriptor pointers_guest entry = pointers_stations-1 pointers_control entry = pointers_stations station entry == record(o f conad+pointers_station displ+stations*station entry size) station entry = default station entry string pt = 1 station entry_name = set string("GUEST") station entry_shortest name = set string(station string(station entry_name)) station entry_max lines = 4 station entry_status = 6 station entry == record(o f conad+pointers_station displ+(stations+1)*station entry size) station entry = default station entry string pt = 1 station entry_name = set string("CONTROL DUMMY") station entry_shortest name = set string(station string(station entry_name)) station entry_max lines = 2 station entry_q lines = 1 station entry_limit = 128 station entry_service = closed station entry_status = 6 { put in GUEST and operational control record } error: finish else printstring("Invalid no of stations :".line.snl) finish else printstring("Failed to find no of stations".snl) !* at this point a config file is created pointers_hash t(i) = -1 for i = 0, 1, hash length pointers_ftp table displ = pointers_station addresses displ+address pt n pt = o f conad+pointers_ftp table displ+(max lines+1)*ftp table entry size generate names(top level, "") cycle i = 1, 1, pointers_stations station entry == record(o f conad+pointers_station displ+(i-1)*station entry size) string pt = 1 string pt = string pt+1+byteinteger(addr(station entry_string space(string pt))) while c byteinteger(addr(station entry_string space(string pt)))#0 if station string(station entry_name) -> (this authority).temps then c station entry_flags = station entry_flags!local host flag if station entry_shortest name = 0 start temp1 = shortest name(i) if length(temp1) > 15 then printstring("SHORTEST NAME too long : ".temp1.snl) and result = 1 station entry_shortest name = set string(temp1) finish repeat set flags(this authority, this auth flag) set flags(this host, this host flag) file header_end = n pt-o f conad ! Actual size file header_type = 0 disconnect(temp output file, flag) if flag=0 start if stations>0 start ; ! Make sure something in it trim(temp output file, flag) if flag=0 start ; ! Successfully trimmed rename(temp output file, output file, flag) newgen(temp output file, output file, flag) if flag=already exists if flag=0 start ; ! Rename / Newgen successful permit(output file, null, read permission, flag) if flag=0 start result = 0 if result=2; ! ie if it has not been set as 'unsuccessful' finish else print string("Set EEP on ".output file." fails ".failure message(flag).snl) finish else start print string("Rename/Newgen ".output file." fails ".failure message(flag).snl) destroy(temp output file, flag); ! To tidy up print string("Destroy ".temp output file." fails ".failure message(flag).snl) unless flag=0 finish finish else print string("Trim ".temp output file." fails ".failure message(flag).snl) finish else print string("No Stations in configuration file".snl) finish else print string("Disconnect ".temp output file." fails ".failure message(flag).snl) finish else printstring("Create and connect ".temp output file." fails ".failure message(flag).snl) finish else printstring("Disconnect/Destroy ".temp output file."fails ".failure message(flag).snl) print string("Failed to compile config".snl) unless result=0; ! unless got to 'centre' of this section result = result end ; ! Of integerfn COMPILE CONFIG !--------------------------------------------------------------------------------------------------------------! routine print defaults integer i, j cycle i = 1, 1, 2 printstring("Defaults for ") if i=1 then printstring("NSI are :".snl) else printstring("TS ( the default address type ) are :".snl) cycle j = 1, 1, n parameters if template(i, j)&ignore=0 and j#3 start printstring(parameter names(j)." : ") if template(i, j)&default=0 then printstring("No default".snl) else c printstring(descriptor defaults(j).snl) finish repeat newline repeat end { of PRINT DEFAULTS } !--------------------------------------------------------------------------------------------------------------! integer fn execute command ! Executes COMMAND ( I ). If command was a successful END command, result is 1, ! ABORT gives result = 2. Illegal command ( eg X-1 ) gives result of -1, unsuccessful ! command ( eg String not found with F, or Insert got a Descriptor too large or No more devices ) ! gives -2, otherwise result is 0. integer i1, j1, a of p, rel a of p, old l of p, new l of p, old len, limit, ign switch command swt(1:no of commands) ->command swt(i) command swt(1): ! Help printstring(snl." ? = Help, at any time F/text/ = Find 'text' ( forwards ),".snl) printstring("U? = Display current macro, F-/text/ = Find 'text' ( backwards ),".snl) printstring(" U = Set up user-defined macro F = Find last used 'text',".snl) printstring(" ( can't contain U,X,A,E,K ) F- = Find last 'text' backwards,".snl) printstring("Xn = Execute macro n times, F? = Display current 'text',".snl) printstring(" ! = Print out defaults, Mn = Move by n descriptors,".snl) printstring("In = Insert n descriptors, K/file/ = Kompile to file, end if OK,".snl) printstring("Dn = Delete n descriptors, A = Abort,".snl) printstring("Cn = Change n descriptors, E = End,".snl) printstring("Pn = Print n descriptors, L/file/ = List to file,".snl) printstring(" T = Change top of file ( for mailer ).".snl.snl) printstring("When inserting, <return> leaves this parameter as defaulted,".snl) printstring(" * leaves this and all following parameters as defaulted.".snl) printstring("When changing, <return> leaves this parameter as set,".snl) printstring(" * leaves this and all following parameters as set,".snl) printstring(" ! sets this parameter to the default.".snl) result = 0 command swt(2): ! Print out n descriptors read value; ! Get number to print out printstring("Invalid parameter for Print".snl) and result = -1 if value=0; ! Can't print out 0 descriptors temp cp = current position; ! Take a copy of CURRENT POSITION to work with if sign<0 start ; ! To print out last n descriptors, move back n descriptors, then print out n. cycle i1 = 1, 1, value; ! Same as Move backwards address = locate(backwards, conad+start position-2, conad+temp cp-2, snl.snl) value = i1-1 and exit if address=0; ! Change number to print out if found top of list prematurely temp cp = (address-conad)+2; ! Change to relative address and skip over double newline repeat finish cycle i1 = 1, 1, value print desc(temp cp) address = locate(forwards, conad+temp cp, conad+end position, snl.snl) ! Find next descriptor exit if address=0; ! Exit if there isn't one temp cp = (address-conad)+2; ! Convert to a relative address and skip over double newline repeat result = 0 command swt(3): ! Move read value; ! Get amount to move by result = 0 if value=0; ! Ignore command if specified move was 0 if sign=1 start ; ! Move forwards cycle i1 = 1, 1, value address = locate(forwards, conad+current position, conad+end position, snl.snl) ! Find address of next double newline before end exit if address=0; ! Don't try to find any more if this was the last one current position = address-conad+2; ! Convert to relative address, and move over double newline repeat finish else start ; ! Move backwards cycle i1 = 1, 1, value address = locate(backwards, conad+start position-2, conad+current position-2, snl.snl) ! Find last double newline between here and start, ignoring the one just before CURRENT POSITION ! ( end of last descriptor ) exit if address=0; ! Don't try to find any more if there aren't any current position = address-conad+2; ! Change to relative address, skip over this double newline repeat finish pdesc = 1; ! Print out descriptor result = 0 command swt(4): ! Find if ch='?' start ; ! Enquire l = l-1 if find string="" then printstring("Not defined".snl) else printstring(find string.snl) result = 0 finish if l>0 and ch='-' then l = l-1 and sign = -1 else sign = 1; ! Set direction of search if l>0 and ch=delimiter start ; ! Read in string l = l-1; ! Remove this character find1 = ""; ! Clear temporary string cycle if ch=delimiter start ; ! Found a delimiter l = l-1; ! Remove it from command line exit unless ch=delimiter; ! End of search string unless next character is also a ! delimiter, in which case ... finish find1 = find1.tostring(ch); ! Add character to string l = l-1; ! Remove it repeat printstring("Invalid string".snl) and result = -1 if find1=""; ! Can't search for a null string find string = find1; ! Copy to search string if string legal finish ; ! ( else search for last used search string ) ! Can't use last used search string if it has not been defined printstring("No string set up for Find".snl) and result = -1 if find string="" if sign=1 then address = locate(forwards, conad+current position, conad+end position, find string) else c address = locate(backwards, conad+start position, conad+current position, find string) ! Find occurrence of string printstring("String not found.".snl) and result = -2 if address=0 address = locate(backwards, conad+start position, address, snl.snl) if address=0 then current position = start position else current position = (address-conad)+2 ! Go back to beginning of the descriptor pdesc = 1; ! Print out descriptor result = 0 command swt(5): ! Insert n descriptors before the current one read value; ! Get number of descriptors to insert printstring("Invalid parameter for Insert".snl) and result = -1 if value=0 or sign=-1 ! Can't insert 0 or a negative number of descriptors if ext file header_stations+value>max stations start ! If maximum reached, adjust VALUE down to fit in value = max stations-ext file header_stations printstring("No more devices".snl) and result = -2 if value=0; ! Unsuccessful command if no room left printstring("Only ".itos(value)." devices may be inserted".snl) finish cycle i1 = 1, 1, value len = 0; ! Descriptor is 0 length as yet addr type = 2 { TS } base type = no cycle j1 = 1, 1, n parameters; ! For each parameter continue unless template(addr type, j1)&ignore=0 dummy = get a param(j1, flag); ! Get parameter flag = 0 and exit if flag=2 { "*" } len = len+printout(1, dummy, len); ! Output to file flag = 0 and exit if base type=yes exit unless flag=0; ! if parameter makes descriptor too large repeat ; ! ( for each parameter ) len = len+print out(1, snl, len) if flag=0; ! Add newline to descriptor if it isn't already too large ! Unsuccessful command if descriptor too large printstring("Descriptor too large.".snl) and result = -2 unless flag=0 move(ext file header_end-current position, conad+current position, conad+current position+len) ! Shift data after CURRENT POSITION up to make room move(len, work conad, conad+current position); ! Move work area into correct place change end position(len) current position = current position+len; ! and CURRENT POSITION ext file header_stations = ext file header_stations+1; ! Increase number of devices change no of devices; ! and change entry at start of text repeat ; ! ( for each descriptor ) result = 0 command swt(6): ! Delete descriptors from here on read value; ! Get number to delete printstring("Invalid parameter for Delete".snl) and result = -1 if value=0 or sign=-1 ! Can't delete 0 or a negative number of descriptors cycle i1 = 1, 1, value exit if ext file header_stations=0; ! If no more devices left to delete address = locate(forwards, conad+current position, conad+end position, snl.snl) ! Find end of this descriptor if address=0 then address = end position else address = (address-conad)+2 ! If double newline not found, set ADDRESS to end ! Change address to relative and skip over double newline move(ext file header_end-address, conad+address, conad+current position) ! Shift data down over this descriptor change end position(current position-address); ! Shift pointers down by ADDRESS - CP ext file header_stations = ext file header_stations-1; ! Decrease number of devices change no of devices; ! and change entry at start of text repeat pdesc = 1; ! Print out descriptor result = 0 command swt(7): ! Change read value printstring("Invalid parameter for CHANGE".snl) and result = -1 if value=0 or sign=-1 pdesc = 1; ! Print out descriptor cycle i1 = 1, 1, value printstring("End of section reached".snl) and exit if current position=end position ! Not allowed to change last descriptor - it must remain empty address = locate(forwards, conad+current position, conad+end position-2, snl.snl) ! Find end of this descriptor if address=0 then address = end position else address = address-conad+2 ! Change to relative address and skip over double newline. Set address to end of this section ! section if end of this descriptor not found ( shouldn't happen ! ) old len = address-current position; ! Old length of this descriptor len = old len; ! New length same as old one to start with move(len, conad+current position, work conad); ! Copy descriptor to work area rel a of p = 0; ! Address of current ( first ) parameter, relative to start of work area addr type = 2 { TS } base type = no cycle j1 = 1, 1, n parameters; ! Round each parameter if base type=yes then ign = 1 else ign = (template(addr type, j1)&ignore) ! If IGN # 0, this parameter is to be removed from descriptor old l of p = 0; ! Initialise length printstring("Current setting of parameter ".ucstring(parameter names(j1))." : ") if ign=0 a of p = locate(forwards, work conad+rel a of p, work conad+len, ucstring(parameter names(j1))) ! Try to find current parameter if a of p#work conad+rel a of p start { If param doesn't start straight away, it is as defaulted } a of p = work conad+rel a of p printstring("As defaulted - ".descriptor defaults(j1).snl) if ign=0 finish else start { work out what param is set to } old l of p = old l of p+1 until byteinteger(a of p+old l of p)='=' ! Count and skip over characters til '=' reached old l of p = old l of p+2; ! Skip over '=' and following space dummy = "" while byteinteger(a of p+old l of p)#nl cycle dummy = dummy.tostring(byteinteger(a of p+old l of p)) old l of p = old l of p+1 repeat printstring(dummy) if ign=0 ! Print out and count characters in this parameter if j1=3 start if ucstring(dummy)="NSI" then addr type = 1 else if ucstring(dummy)="BASE" then base type = yes finish { Presumably, if J1 = 3, IGN = 0 for either ADDR TYPE so no need to reassign IGN } old l of p = old l of p+1; ! Skip over newline unless template(addr type, j1)&multiple=0 start ; ! Find multiple params if there are any cycle address = locate(forwards, a of p+old l of p, work conad+len, ucstring(parameter names(j1))) ! Try to find parameter name again exit unless address=a of p+old l of p; ! Ignore it unless it starts immediately printstring(" and ") if ign=0 old l of p = old l of p+1 until byteinteger(a of p+old l of p)='=' ! Count and skip over chars til '=' found old l of p = old l of p+2; ! Skip over '=' and following space while byteinteger(a of p+old l of p)#nl cycle printch(byteinteger(a of p+old l of p)) if ign=0 old l of p = old l of p+1 repeat ! Count and print out characters in rest of parameter old l of p = old l of p+1; ! Skip over newline repeat ; ! Get all parts of multiple parameter finish newline finish prompt(parameter names(j1).":") dummy = "" count = 0; ! No of parameters input so far limit = 1+template(addr type, j1)>>4; ! Max no of multiple inputs ( if allowed ) if ign#0 then dummy = "" else start cycle read line if line1="?" start ; ! Help needed printstring(help info(j1).snl) printstring("Legal replies are : * to exit, ! to leave this parameter as defaulted, <return> to leave this parameter as set, <text> to set this parameter to 'text' ".snl) continue ; ! and get another input finish dummy = line1 and exit if (line1="*" or line1="") and count=0 ! Leave parameter as it is ( and maybe rest of descriptor too ) if ucstring(line1)=".END" and count=0 then printstring(".END not legal here".snl) and continue ! Can't use .END as first input - only to end multiple inputs after at least one has been entered if line1="!" start printstring(parameter names(j1)." does not have a default".snl) and continue if c template(addr type, j1)&default=0 base type = no and addr type = 2 if j1=3 { set default address type } dummy = "" ign = 1 { this parameter now to be ignored, ie removed from descriptor } exit finish if length(dummy)+parm(j1, line1, dummy1)>255 and ucstring(line1)#".END" then c printstring("Parameter too long".snl) and dummy = "" and count = 0 and continue ! Adding this parameter to what we already have would make DUMMY longer than 255 chars if j1=3 start base type = no if ucstring(line1)="NSI" then addr type = 1 else if ucstring(line1)="TS" then c addr type = 2 else if ucstring(line1)="BASE" then base type = yes finish if template(addr type, j1)&multiple=0 then dummy = dummy1 and exit else start ! DUMMY1 is output from PARM - exit if only single inputs allowed count = count+1; ! One more input has been added exit if ucstring(line1)=".END"; ! Finished inputting dummy = dummy.dummy1; ! otherwise add to parameter continue ; ! and get next part finish repeat until count=limit; ! Maximum of LIMIT parts for multiple parameters finish rel a of p = rel a of p+old l of p and continue if dummy="" and ign=0 ! Skip over this parameter if it is to be left exit if dummy="*"; ! Quit changing this descriptor if * typed if len-old l of p+length(dummy)>max desc size then c printstring("Descriptor too large".snl) and exit else start move(len-old l of p-rel a of p, a of p+old l of p, a of p+length(dummy)) ! Make room for this parameter in work area new l of p = printout(1, dummy, rel a of p); ! Copy parameter to work area len = len-old l of p+new l of p; ! Adjust length of this descriptor rel a of p = rel a of p+new l of p; ! and skip over to next one finish repeat ; ! For each parameter of this descriptor move(ext file header_end-current position-old len, conad+current position+old len, conad+current position+len) ! Make room in main file for it move(len, work conad, conad+current position); ! Copy descriptor to main file change end position(len-old len); ! Adjust pointers by difference in descriptor lengths current position = current position+len; ! And skip over this descriptor to next one repeat ; ! For each descriptor to be CHANGEd result = 0 command swt(8): ! Set up User-defined macro printstring("Can't use U within a macro.".snl) and result = -1 unless macro=0 ! Set 'illegal command' flag if user tried to define a macro within a macro if ch='?' start ; ! Enquire l = l-1; ! Remove ? from LINE enquire: if macro string="" then printstring("Not defined".snl) else start printch(charno(macro string, i1)) for i1 = length(macro string), -1, 1 ! Print out macro ( it is stored backwards like command line ) newline finish result = 0; ! And return finish prompt("Macro defn : ") read command(dummy); ! Input macro definition if dummy="?" then ->enquire else macro string = dummy; ! If response was ?, user wants to see current macro, ! otherwise response was new macro result = 0 command swt(9): ! EXecute user-defined macro printstring("Can't use X within a macro.".snl) and result = -1 unless macro=0 ! Can't nest execution of macros, so give 'illegal command' result printstring("Illegal parameter for EXECUTE".snl) and l = l-1 and result = -1 if ch='*' ! Can't X* ( might get caught in an endless loop ) read value; ! Get number of times to execute macro printstring("Illegal parameter for EXECUTE".snl) and result = -1 if value<0 or sign=-1 ! Can't execute macro zero or negative number of times macro = 1; ! Now executing macro ( can't do things like defining macro ) save string = line; ! Save command line ... cycle j = 1, 1, value; ! ... and for each execution of the macro ... line = macro string; ! ... copy macro into it while l>0 cycle ; ! ie while there is still some macro left to execute flag = 3; ! Command not found yet cycle i = 1, 1, no of commands; ! Check command against legal ones flag = execute command and exit if ch=command(i); ! If found command repeat ; ! for each legal command printstring("Illegal command.".snl) if flag=3; ! ie if command wasn't found exit if flag=-2; ! If, for example, got a 'String not found' printstring("Illegal macro".snl) and macro string = "" and exit unless flag=0 ! Abandon macro if illegal command found repeat exit unless flag=0; ! ie exit if anything untoward happened repeat ; ! for each execution of macro line = save string; ! Copy command line back macro = 0; ! ... and reset flag result = flag command swt(10): ! Print out defaults print defaults result = 0 command swt(11): ! Abort printstring("Can't use A within a macro.".snl) and result = -1 unless macro=0 ! Set 'illegal command' if within a macro printstring("A or Y to abort, anything else to return to program.".snl) prompt("Abort : ") read command(dummy); ! Find out if abort was intentional if dummy="A" or dummy="Y" then result = 2 else result = 0; ! If it was, set result to ! successful abort, otherwise ignore command command swt(12): ! End printstring("Can't use E within a macro.".snl) and result = -1 unless macro=0 ! Set 'illegal command' if tried to END from within a macro printstring("No stations".snl) if ext file header_stations=0 ! Inform user if any section of file is devoid of entries result = 1; ! Successful END command swt(13): ! Compile printstring("Can't use K within a macro.".snl) and result = -1 unless macro=0 if ch=delimiter start l = l-1 output file = "" while ch<>delimiter and length(output file)<11 cycle output file = output file.tostring(ch) l = l-1 repeat if ch<>delimiter or output file="" start printstring("Illegal output file name".snl) result = -1 finish else default output file = output file and l = l-1 { remove last delimiter } finish else output file = default output file if exist(output file)#0 start printstring(output file." already exists and will be overwritten. Is this OK ?".snl) prompt("Y/K to compile ") read command(dummy) result = 0 unless dummy="Y" or dummy="K" finish if compile config(output file)=0 start printstring("Compiled config is in file ".output file.snl) result = 1 { END } finish else result = -2 { failed to compile so return to editor } command swt(14): ! List if ch=delimiter start l = l-1 list file = "" while ch<>delimiter and length(list file)<11 cycle list file = list file.tostring(ch) l = l-1 repeat if ch<>delimiter or list file="" start printstring("Illegal list file name".snl) result = -1 finish else l = l-1 finish else list file = default list file if exist(list file)#0 start printstring(list file." already exists and will be overwritten. OK ?".snl) prompt("Y / L to list") read command(dummy) result = 0 unless dummy="Y" or dummy="L" finish define("1,".list file) if return code=0 start select output(1) if return code=0 start print defaults printch(byteinteger(conad+i1)) for i1 = ext file header_start, 1, ext file header_end finish else printstring("Failed to select output stream :".failure message(return code).snl) select output(0) finish else printstring("Failed to define stream :".failure message(return code).snl) if return code=0 then result = 0 else result = -2 command swt(15): ! Repeat if ch=delimiter start dummy = "" l = l-1 cycle if ch=delimiter start l = l-1 exit unless ch=delimiter finish dummy = dummy.tostring(ch) l = l-1 repeat if dummy="" or length(dummy)>247 start printstring("Illegal name to REPEAT".snl) result = -1 finish finish else start printstring("REPEAT must be followed by a name".snl) result = -1 finish address = locate(forwards, conad+ext file header_start, conad+ext file header_end, "NAME = ".dummy.snl) if address=0 start printstring("Name ".dummy." not found".snl) result = -2 finish len = locate(forwards, address, conad+ext file header_end, snl.snl)+2-address move(len, address, work conad) move(ext file header_end-current position, conad+current position, conad+current position+len) move(len, work conad, conad+current position) change end position(len) ext file header_stations = ext file header_stations+1 change no of devices result = 0 command swt(16): ! Change top of file address = conad+ext file header_start cycle i1 = 0, 1, 3 dummy = "" dummy = dummy.tostring(byteinteger(address+j1)) for j1 = 0, 1, 79 printstring(dummy) prompt(substring(dummy, 1, 15)) read line exit if line1="*" if line1#"" start length(line1) = 63 if length(line1)>63 line1 = line1." " while length(line1)<63 move(63, addr(line1)+1, address+15) finish address = address+80 repeat result = 0 end ; ! of integerfn EXECUTE COMMAND !-------------------------------------------------------------------------------------------------------------------! filename = file unless file->owner.(".").filename if length(filename)<11 then default output file = filename else default output file = substring(filename, 1, 10) default list file = default output file."L" default output file = default output file."Y" this full host = uinfs(15).".".uinfs(16) flag2 = 0; ! To force destroying workfile in case of crash if exist(file)#0 start copy(file.",".workfile) if return code=0 start change file size(workfile, x'44000', flag) if flag=0 start connect(workfile, read and write, 0, 0, r, flag) if flag=0 then conad = r_conad else c printstring("Failed to connect ".workfile." : ".failure message(flag).snl) finish else printstring("Failed to change size of ".workfile." : ".failure message(flag).snl) flag1 = 1 { Set flag for 'already existed' } finish else printstring("Failed to copy ".file." : ".failure message(return code).snl) and flag = -1 finish else start { File doesn't exist } outfile(workfile, x'44000', 0, X'80000000', conad, flag) printstring("Failed to create and connect ".workfile." : ".failure message(flag).snl) unless flag=0 flag1 = 0 { new file } finish if flag=0 start ; ! If first section successfully completed ext file header == record(conad); ! Map EXT FILE HEADER. ext file header_datetime = pack date and time(date, time); ! Set date and time on file if exist(insertfile)#0 then destroy(insertfile, flag) else flag = 0 ! Destroy insertfile if it exists if flag=0 start ; ! If insertfile ready to be created and connected outfile(insertfile, max desc size, 0, X'80000000', work conad, flag) if flag=0 start ; ! If successfully created and connected if flag1=0 then start ; ! New file printstring(file." is a new file".snl) ext file header_checkword = X'18061966'; ! Random integer to check if file is really an modftrans file ext file header_size = x'44000'; ! 200 Kbytes ( plus a bit ) ext file header_type = 4; ! Character file ext file header_start = ext file header size ext file header_stations = 0 current position = ext file header_start+print out(0, padout("IPL discs =", 79).snl.padout("Update = ", 79).snl, ext file header_start) current position = current position+print out(0, padout("This host = ", 79).snl.padout("Dead letters = ", 79).snl."Stations = 0 ".snl.snl.snl.snl, current position) start position = current position-2 end position = current position-2 ext file header_end = current position; ! End of file finish else start ; ! File already exists printstring(file." is not a modftrans file.".snl) and ->return unless c ext file header_checkword=X'18061966' ! Check that file was created by this program ( EXT FILE HEADER_CHECKWORD is set if FILE is a new file ) address = locate(forwards, conad+ext file header_start, conad+ext file header_end, "STATIONS =") unless address=0 start address = locate(forwards, address, conad+ext file header_end, snl.snl) start position = address+2-conad unless address=0 finish if address=0 then printstring("Failed to find start of stations.".snl. c "There must be a line containing 'Stations ='; the ' ' is necessary.".snl) and ->return address = locate(forwards, address, conad+ext file header_end, snl.snl.snl.snl) printstring("Failed to find end of stations.".snl) and ->return if address=0 end position = address+2-conad finish printstring("Type '?' for help info.".snl) l == length(line) l = 0 current position = start position find string = ""; ! Clear Search string ... macro string = ""; ! ... and macro macro = 0; ! ie not executing macro as yet pdesc = 0; ! Don't print out descriptor yet cycle pdesc = 0 and print desc(current position) if l=0 and pdesc=1 ! Print out descriptor if PDESC is 1 and LINE is null ( finished ) prompt("Config : ") read command(line) while l=0; ! Ignore null command flag2 = 3; ! ie command not found yet cycle i = 1, 1, no of commands; ! Go to appropriate routine if ch=command(i) start l = l-1 flag2 = execute command exit finish repeat exit if flag2=1 or flag2=2; ! if successful ABORT or END printstring("Illegal command ".tostring(ch).snl) and l = 0 if flag2=3; ! Stop if illegal l = 0 if flag2<0; ! Ignore rest of command line if a command failed for some reason repeat return: disconnect(insertfile, flag); ! Disconnect insertfile destroy(insertfile, flag) if flag=0 printstring("Disconnect/destroy insertfile ".insertfile." fails : ".failure message(flag).snl) unless c flag=0 finish else printstring("Create and connect insertfile ".insertfile." fails : ".failure message(flag).snl) finish else printstring("Destroy insertfile ".insertfile." fails : ".failure message(flag).snl) ext file header_size = ((ext file header_end+4095)>>12)<<12; ! Set new size disconnect(workfile, flag) if flag=0 start ; ! If workfile successfully disconnected if flag2=1 start ; ! Exited from program because user typed E trim(workfile, flag) printstring("Trim ".workfile." fails : ".failure message(flag).snl) unless flag=0 rename(workfile, file, flag); ! Rename workfile to FILE newgen(workfile, file, flag) if flag=already exists; ! Newgen if FILE already exists if flag=0 then printstring("modftrans ".file." completed.".snl) else start printstring("Failed to copy ".workfile." to ".file." : ".failure message(flag).snl) printstring("Editing is saved in file ".workfile.snl) finish finish else start ; ! Exited from program because user typed A destroy(workfile, flag) if flag=0 then printstring("modftrans ".file." aborted.".snl) else c printstring("Failed to destroy ".workfile." : ".failure message(flag).snl) finish finish else printstring("Disconnect workfile ".workfile." fails : ".failure message(flag).snl) finish end ; ! of externalroutine modftrans !--------------------------------------------------------------------------------------------------------------! end of file