u3369429@murdu.UUCP (10/01/87)
I have received a number of reports on VMS_SHAR and incorporated most of the suggestions, notably that all control characters are transmitted as quoted decimal numbers. Question to TPU gurus: How does one find out the decimal ASCII value of a character? Or in other words: is there a reverse function to ASCII which - strangely enough - returns a character from a given integer? Here is the blurb, followed by VMS_SHAR.COM and VMS_SHAR.READ_ME: VMS_SHAR is an attempt to provide a tool to package source modules for mail transmission. It will package any number of files into a self-unpacking procedure. A number of steps are taken to insure file integrity. VMS_SHAR is convenient for applications consisting of a number of source modules, because they are automatically created at the receiver's end; it is necessary because various mailers may mutilate source code in a number of ways: o split lines which are longer than 80 characters, o remove leading white space; o munge control characters like <TAB>, <ESC>, or <BEL>, o choke on 8-bit characters. Features: o Through the use of SET SYMBOL/SCOPE=(NOLOCAL,NOGLOBAL) both VMS_SHAR and the procedures created by it are very rubust against local idiosyncrasies with symbols. Imagine a user who does: IF=="!" ! o The resulting file gives the username of its creator and date/time of creation. If the global symbol REAL_NAME exists, its content is also displayed. o The resulting file contains a list of all its files. o Each line is prepended by character "X" or "V" to preserve leading spaces through various mailers. o Lines longer than 79 characters will be split (and are prepended by "V"). o All non-printable ASCII characters (0-31,127-255) are transmitted as quoted decimal numbers. o Some mailers disallow postings above a certain size. VMS_SHAR will check that the resulting file will not exceed MAX_PART_SIZE which is currently set to 31 blocks (our mailer disallows postings >16000 characters). If more than 31 blocks are to be posted, VMS_SHAR will create multiple files by appending a 'part number' to the output file name. If one single input file exceeds MAX_PART_SIZE the user will be notified, and 'part number' will be incremented such that subsequent manual splitting has free 'part numbers' available. If the logical name SHAR_MAX_SIZE is defined as a number, that number will be used as MAX_PART_SIZE. o At both creation and unpacking time, each file is CHECKSUMmed; the result of the comparison of both values is displayed to the user. o Signatures and such, which might occur at the end of each part, don't need to be stripped. They are skipped by the unpacking procedure. o The created procedure will check for VMS version 4.4 or higher. ....................... Cut between dotted lines and save ...................... $!.............................................................................. $! VAX/VMS archive file created by VMS_SHAR V-5.01 01-Oct-1987 $! which was written by Michael Bednarek (U3369429@ucsvc.dn.mu.oz.au) $! To unpack, simply save and execute (@) this file. $! $! This archive was created by U3369429 (Michael Bednarek) $! on Thursday 1-OCT-1987 10:10:02.71 $! $! It contains the following 2 files: $! VMS_SHAR.COM VMS_SHAR.READ_ME $!============================================================================== $ Set Symbol/Scope=(NoLocal,NoGlobal) $ Version=F$GetSYI("VERSION") ! See what VMS version we have here: $ If Version.ges."V4.4" then goto Version_OK $ Write SYS$Output "Sorry, you are running VMS ",Version, - ", but this procedure requires V4.4 or higher." $ Exit 44 $Version_OK: CR[0,8]=13 $ Pass_or_Failed="failed!,passed." $ Goto Start $Convert_File: $ Read/Time_Out=0/Error=No_Error1/Prompt="creating ''File_is'" SYS$Command ddd $No_Error1: Define/User_Mode SYS$Output NL: $ Edit/TPU/NoSection/NoDisplay/Command=SYS$Input/Output='File_is' - VMS_SHAR_DUMMY.DUMMY f:=Get_Info(Command_Line,"File_Name");b:=Create_Buffer("",f); o:=Get_Info(Command_Line,"Output_File");Set(Output_File,b,o); Position(Beginning_of(b));Loop x:=Erase_Character(1);Loop ExitIf x<>"V"; Move_Vertical(1);x:=Erase_Character(1);Append_Line; Move_Horizontal(-Current_Offset);EndLoop;Move_Vertical(1); ExitIf Mark(None)=End_of(b) EndLoop;Position(Beginning_of(b));Loop x:=Search("`",Forward,Exact);ExitIf x=0;Position(x);Erase_Character(1); If Current_Character='`' then Move_Horizontal(1);else Copy_Text(ASCII(INT(Erase_Character(3))));EndIf;EndLoop;Exit; $ Delete VMS_SHAR_DUMMY.DUMMY;* $ Checksum 'File_is $ Success=F$Element(Check_Sum_is.eq.CHECKSUM$CHECKSUM,",",Pass_or_Failed)+CR $ Read/Time_Out=0/Error=No_Error2/Prompt=" CHECKSUM ''Success'" SYS$Command ddd $No_Error2: Return $Start: $ File_is="VMS_SHAR.COM" $ Check_Sum_is=672593845 $ Copy SYS$Input VMS_SHAR_DUMMY.DUMMY X$ Verify=F$Verify(F$TRNLNM("COMMAND_DEBUG")) X$ Facility_Name= "VMS_SHAR" X$ Facility_Version= "V-5.01 01-Oct-1987" X$! X$ SS$_Abort=44 X$ On Control_Y then goto Abort X$ On Error then Continue X$! X$!Michael Bednarek u3369429@{murdu.oz.au | ucsvc.dn.mu.oz.au} X$!Institute of Applied Economic -- or -- X$! and Social Research (IAESR) ...{UUNET.UU.NET | seismo.CSS.GOV}!munnari! X$!Melbourne University {murdu.oz | ucsvc.dn.mu.oz}!u3369429 X$!Parkville 3052, Phone : +61 3 344 5744 X$!AUSTRALIA X$! X$! Copyright (c) 1987, by Michael Bednarek X$! The distribution of this file is unrestricted as long as this notice X$! remains intact. X$! Credits: SLOANE@UKANVAX@BITNET (Bob Sloane) for the idea of X$! quoting control characters. X$! X$! Usage: @VMS_SHAR file[,file...] sharfile X$! where file are file names, separated by commas, possibly including X$! wild-card characters, of those files that are to be packaged. X$! sharfile is the resulting self-unpacking archive file. X$! VAX/VMS version 4.4 or higher X$! X$ User=F$Edit(F$GetJPI("","USERNAME"),"COLLAPSE") X$ If F$Type(Real_Name).nes."" then User=User+" ("+Real_Name+")" X$ Set="Set" X$ Set Symbol/Scope=(NoLocal,NoGlobal) X$ Say="Write SYS$Output" X$ Say Facility_Name," ",Facility_Version X$ Say "" X$ If P1.eqs."" then Inquire P1 "_File(s) to package" X$ If P1.eqs."" then Exit SS$_Abort X$ If P2.eqs."" then Inquire P2 "_Output Archive" X$ If P2.eqs."" then Exit SS$_Abort X$ File_List=P1 X$ Shar_File=F$Parse(P2)-";" X$ MAX_PART_SIZE=31 ! 31 blocks will keep us below 16000 bytes X$ x=F$Integer(F$TRNLNM("SHAR_MAX_SIZE")) ! Allow the user to override X$ If x.gt.1 then MAX_PART_SIZE=x ! this parameter X$ Say "MAX_PART_SIZE is defined as ",MAX_PART_SIZE," blocks." X$ Say "" X$! As this procedure adds a 'X' in the first position of every line, the X$! resulting file can still become larger than MAX_PART_SIZE X$ BEL[0,7]=7 X$ nParts=1 X$ Part_Size=5 ! startup code, erring on the conservative side X$ Surprise="False" X$ nE=-1 X$ nF=1 X$Next_Element: nF=nF-1 ! Collect all filenames X$ nE=nE+1 X$ Element=F$Element(nE,",",File_List) X$ If Element.eqs."," then goto Elements_Done X$ Previous_File="no file" X$Next_File: nF=nF+1 X$ File'nF=F$Search(Element) X$ If File'nF.eqs.Previous_File then goto Next_Element X$ If File'nF.nes."" then goto Look X$ If Previous_File.eqs."no file" then - X Say "%VMS_SHAR-I-FNF, file not found: ",Element X$ Goto Next_Element X$Abort: Close/Error=Anyway Out X$Anyway:Exit SS$_Abort X$Look: Previous_File=File'nF X$ Say "looking at ",File'nF X$ If F$Element(0,";",File'nF).nes.Shar_File then goto Not_Silly X$ Say "You can't have your outputfile among the input files!" X$ Exit SS$_Abort X$Not_Silly: Size'nF=F$File_Attributes(File'nF,"EOF") X$ Part_Size=Part_Size+Size'nF X$ If Part_Size.le.MAX_PART_SIZE then goto Next_File X$ nParts=nParts+1 X$ Part_Size=Size'nF X$ Goto Next_File X$Elements_Done: If nF.gt.0 then goto Intro X$ Say "%VMS_SHAR-W-SEARCHFAIL, error searching for",P1 X$ Say "-VMS_SHAR-E-FNF, file not found. No file to package. Nothing done." X$ Exit SS$_Abort X$Intro: Time=F$CVTime(,,"Weekday")+" "+F$CVTime(,"Absolute","DateTime") X$ Part_Name="" X$ Of_Name="" X$ If nParts.lt.2 then goto Only_One1 X$ Of_Name=F$FAO("_OF_!ZL",nParts) X$ i=1 ! i=LOG10(nParts)+1 X$ If nParts.gt.9 then i=2 X$ If nParts.gt.99 then i=3 ! We don't expect more than 999 files X$ Part_Name=F$FAO("!#ZL",i,1) X$Only_One1: Copy NL: 'Shar_File''Part_Name''Of_Name X$ Open/Append Out 'Shar_File''Part_Name''Of_Name X$ Write Out F$FAO("!23*. Cut between dotted lines and save !22*.") X$ Write Out F$FAO("$!!!78*.") X$ Write Out "$! VAX/VMS archive file created by ",Facility_Name," ", - X Facility_Version X$ Write Out "$! which was written by Michael Bednarek ",- X "(U3369429@ucsvc.dn.mu.oz.au)" X$ Write Out "$! To unpack, simply save and execute (@) this file. X$ Write Out "$!" X$ Write Out "$! This archive was created by ''User'" X$ Write Out "$! on ''Time'" X$ Write Out "$!" X$ If nParts.lt.2 then goto Only_One2 X$ Write Out "$! ATTENTION: To keep each article below ", - X F$Integer(MAX_PART_SIZE*512)," bytes, this program" X$ Write Out "$! has been transmitted in ",nParts," parts." X$ Write Out "$! You should concatenate ALL parts to ONE file and execute ", - X "(@) that file." X$ Write Out "$!" X$Only_One2: Write Out F$FAO("$!! It contains the following !UL file!%S:",nF) X$ Files="" X$ n=0 X$Next_Name: n=n+1 X$ Name=F$Parse(File'n,,,"NAME")+F$Parse(File'n,,,"TYPE") X$ If F$Length(Files)+F$Length(Name).le.77 then goto Same_Line X$ Write Out "$!",Files X$ Files="" X$Same_Line: Files=Files+" "+Name X$ If n.lt.nF then Goto Next_Name X$ Write Out "$!",Files X$ Close Out X$ Append SYS$Input 'Shar_File''Part_Name''Of_Name X$ Deck/Dollars="SUBROUTINE_EOF" V$!============================================================================= X= X$ Set Symbol/Scope=(NoLocal,NoGlobal) X$ Version=F$GetSYI("VERSION") ! See what VMS version we have here: X$ If Version.ges."V4.4" then goto Version_OK X$ Write SYS$Output "Sorry, you are running VMS ",Version, - X ", but this procedure requires V4.4 or higher." X$ Exit 44 X$Version_OK: CR[0,8]=13 X$ Pass_or_Failed="failed!,passed." X$ Goto Start X$Convert_File: X$ Read/Time_Out=0/Error=No_Error1/Prompt="creating ''File_is'" SYS$Command ddd X$No_Error1: Define/User_Mode SYS$Output NL: X$ Edit/TPU/NoSection/NoDisplay/Command=SYS$Input/Output='File_is' - X VMS_SHAR_DUMMY.DUMMY Xf:=Get_Info(Command_Line,"File_Name");b:=Create_Buffer("",f); Xo:=Get_Info(Command_Line,"Output_File");Set(Output_File,b,o); XPosition(Beginning_of(b));Loop x:=Erase_Character(1);Loop ExitIf x<>"V"; XMove_Vertical(1);x:=Erase_Character(1);Append_Line; XMove_Horizontal(-Current_Offset);EndLoop;Move_Vertical(1); XExitIf Mark(None)=End_of(b) EndLoop;Position(Beginning_of(b));Loop Xx:=Search("``",Forward,Exact);ExitIf x=0;Position(x);Erase_Character(1); XIf Current_Character='``' then Move_Horizontal(1);else XCopy_Text(ASCII(INT(Erase_Character(3))));EndIf;EndLoop;Exit; X$ Delete VMS_SHAR_DUMMY.DUMMY;* X$ Checksum 'File_is X$ Success=F$Element(Check_Sum_is.eq.CHECKSUM$CHECKSUM,",",Pass_or_Failed)+CR X$ Read/Time_Out=0/Error=No_Error2/Prompt=" CHECKSUM ''Success'" SYS$Command ddd X$No_Error2: Return X$Start: XSUBROUTINE_EOF X$ n=0 X$Extract_Next: n=n+1 X$ Name=F$Parse(File'n,,,"NAME")+F$Parse(File'n,,,"TYPE") V$ Part_Size=F$File_Attributes("''Shar_File'''Part_Name'''Of_Name'","EOF")+Size' Xn X$ If Part_Size.le.MAX_PART_SIZE then goto Same_Part1 X$ Open/Append Out 'Shar_File''Part_Name''Of_Name X$! Warning: this will not work if in the course of packaging the files it X$! turns out that by adding all those "X"s an archive suddenly needs more than X$! one part. The probability for this is rather low, though, because the X$! combined files are on average 256*(Number of files) bytes shorter than X$! their individual block numbers added. X$ If F$Type(i).nes."" then goto No_Surprise X$ Surprise="True" X$ Goto Surprise X$No_Surprise: Part_Name=F$FAO("!#ZL",i,F$Integer(Part_Name)+1) X$Surprise: If Size'n.le.MAX_PART_SIZE then goto Fits X$ Say "" X$ Say "The file ",File'n X$ Say "is ",F$Integer(Size'n-MAX_PART_SIZE),- X " blocks larger than MAX_PART_SIZE.",BEL X$ Say "You will have to carve up that file manually." X$ Say "I'll attempt to increment the part number appropriately" X$ Current_Part=F$Integer(Part_Name) X$ Part_Name=F$FAO("!#ZL",i,F$Integer(Size'n/MAX_PART_SIZE)+Current_Part) X$ Current_Part=F$FAO("!#ZL",i,Current_Part) X$ Say "The file ",Shar_File,Part_Name,Of_Name," should be split into",BEL X$ Say Shar_File,Current_Part,Of_Name," to ",Shar_File,Part_Name,Of_Name X$ Say "NOTE: the common suffix ",Of_Name," is now also wrong." X$ Say "I'm afraid you have to rename all files manually." X$ Read/End_of_File=Fits/Prompt="Enter <RETURN> to continue " SYS$Command ddd X$Fits: Write Out "$ Goto Part''Part_name'" X$ Close Out X$ Say "" X$ Say "Now writing to ",Shar_File,Part_Name,Of_Name X$ Copy NL: 'Shar_File''Part_Name''Of_Name X$ Open/Append Out 'Shar_File''Part_Name''Of_Name X$ Write Out "$Part''Part_Name':" X$Same_Part1: Say "appending ",File'n X$ If F$TRNLNM("Out").eqs."" then Open/Append Out 'Shar_File''Part_Name''Of_Name X$ Write Out "$ File_is=""''Name'""" X$ Checksum &File'n X$ Write Out "$ Check_Sum_is=''CHECKSUM$CHECKSUM'" X$ Write Out "$ Copy SYS$Input VMS_SHAR_DUMMY.DUMMY" X$ Close Out X$ If .not.F$Verify() then Define/User_Mode SYS$Output NL: X$ Edit/TPU/NoSection/NoDisplay/Command=SYS$Input/Output=VMS_SHAR_DUMMY.DUMMY - X &File'n Xf:=0; ! Initialise variable for ASCII decimals, Xk:=""; ! and string which will contain all control characters. XLoop ! Construct a string with all control characters. X k:=k+ASCII(f); ! Generate a control character and add it to "k" X ExitIf f=255; ! Highest ASCII decimal. X If f=31 then ! Adjust for X f:=127; ! the non-control set. X else X f:=f+1; ! Increment ASCII decimal. X EndIf; XEndLoop; Xcb:=Create_Buffer("cb"); XPosition(Beginning_of(cb)); XCopy_Text(k); ! Write string with all control characters into buffer "cb", Xkp:=Any(k); ! and use it as a pattern for incremental search. X Xf:=Get_Info(Command_Line,"File_Name"); Xb:=Create_Buffer("",f); XSet(Output_File,b,File_Parse(Get_Info(Command_Line,"Output_File"))); XPosition(Beginning_of(b)); Xq:="``" & (ARB(1)|Line_End); ! Pattern for quotes X! This pattern may seem elaborate, but it allows positioning AFTER the quote, X! so we don't have to skip it. XLoop X x:=Search(q,Forward,Exact); ! First, quote all quotes. X Exitif x=0; ! No (more) quotes. X Position(End_of(x)); ! Position after the quote, X Copy_Text("``"); ! insert another quote, XEndLoop; X XPosition(Beginning_of(b)); XLoop ! Secondly, replace all control characters X x:=Search(kp,Forward,Exact); ! with their quoted decimal ASCII values. X ExitIf x=0; ! No (more) control characters. X Position(x); ! Position on the control character, X x:=Erase_Character(1); ! delete and remember it (in "x"), X Position(Beginning_of(cb)); ! in buffer "cb", X Position(Search(x,forward,Exact)); ! find its X x:=Current_Offset; ! position and remember it (in "x"). X If x>31 then ! Adjust the position X x:=x+95; ! for the non-control set. X EndIf; X Position(b); ! Back to our main buffer X Copy_Text("``"+FAO("!3ZL",x)) ! where the quoted decimal ASCII value X EndLoop; ! is written. X XPosition (Beginning_of(b)); XLoop ! Thirdly, all lines longer than 79 X If Length(Current_Line)>79 then ! characters are X Copy_Text("V"); ! prepended with a "V" X Move_Horizontal(79); ! and X Split_Line; ! split. X else ! Shorter lines are X Copy_Text("X"); ! prepended with a "X" X Move_Horizontal(-1); ! (which moves our position) X Move_Vertical(1); ! and we advance to the next line. X EndIf; X ExitIf Mark(None)=End_of(b); XEndLoop; XExit; X$ Append VMS_SHAR_DUMMY.DUMMY 'Shar_File''Part_Name''Of_Name X$ Delete VMS_SHAR_DUMMY.DUMMY;* X$ Append SYS$Input 'Shar_File''Part_Name''Of_Name X$ Deck/Dollars="Just_1" X$ GoSub Convert_File XJust_1 X$ If n.lt.nF then goto Extract_Next X$ Append SYS$Input 'Shar_File''Part_Name''Of_Name X$ Deck/Dollars="The End" X$ Exit XThe End X$ If Surprise then Part_Name="2" X$ If Part_Name.eqs."" then Part_Name="1" X$ If .not.Surprise .and. nParts.eq.F$Integer(Part_Name) then goto No_Part_Warn X$ Say "ATTENTION:",BEL X$ Say "Unfortunately, it turns out that the initial estimate about the number" X$ Say "of shar-files was wrong." X$ If Part_Name.eqs."" then Part_Name="2" X$ Say F$FAO("Instead of !SL part!%S there are really ''Part_Name'.",nParts) X$ nParts=F$Integer(Part_Name) X$No_Part_Warn: X$ Say F$FAO("SHAR-file ''Shar_File' was written in !SL part!%S",nParts) X$ Directory/Size=Used/Date=Created 'Shar_File'* X$ Verify=F$Verify(Verify) $ GoSub Convert_File $ File_is="VMS_SHAR.READ_ME" $ Check_Sum_is=1095089683 $ Copy SYS$Input VMS_SHAR_DUMMY.DUMMY XVMS_SHAR.READ_ME: X XVMS_SHAR is an attempt to provide a tool to package source modules for Xmail transmission. It will package any number of files into a self-unpacking Xprocedure. A number of steps are taken to insure file integrity. X XVMS_SHAR is convenient for applications consisting of a number of source Xmodules, because they are automatically created at the receiver's end; Xit is necessary because various mailers may mutilate source code in a Xnumber of ways: X o split lines which are longer than 80 characters, X o remove leading white space; X o munge control characters like <TAB>, <ESC>, or <BEL>, X o choke on 8-bit characters. X XFeatures: X o Through the use of SET SYMBOL/SCOPE=(NOLOCAL,NOGLOBAL) both VMS_SHAR X and the procedures created by it are very rubust against local X idiosyncrasies with symbols. Imagine a user who does: IF=="!" ! X o The resulting file gives the username of its creator and date/time X of creation. If the global symbol REAL_NAME exists, its content is X also displayed. X o The resulting file contains a list of all its files. X o Each line is prepended by character "X" or "V" to preserve leading spaces X through various mailers. X o Lines longer than 79 characters will be split (and are prepended by "V"). X o All non-printable ASCII characters (0-31,127-255) are transmitted as X quoted decimal numbers. X o Some mailers disallow postings above a certain size. VMS_SHAR will check X that the resulting file will not exceed MAX_PART_SIZE which is currently X set to 31 blocks (our mailer disallows postings >16000 characters). X If more than 31 blocks are to be posted, VMS_SHAR will create multiple X files by appending a 'part number' to the output file name. X If one single input file exceeds MAX_PART_SIZE the user will be notified, X and 'part number' will be incremented such that subsequent manual splitting X has free 'part numbers' available. X If the logical name SHAR_MAX_SIZE is defined as a number, that number X will be used as MAX_PART_SIZE. X o At both creation and unpacking time, each file is CHECKSUMmed; the result X of the comparison of both values is displayed to the user. X o Signatures and such, which might occur at the end of each part, don't X need to be stripped. They are skipped by the unpacking procedure. X o The created procedure will check for VMS version 4.4 or higher. X XAbout Version 2.00: XMainly a cleaning-up re-write. Fixed the case where full pathnames were Xgiven. Also checks now whether the output filename is among the input Xfilenames. XThe procedure now performs in two distinct steps: X a) collecting file names and their size information, X b) actually packaging the files. XThis allows to include an indication of the number of parts at the top Xof the resulting file. It also allows to omit part-suffixes if not required. XHowever, under rare circumstances this might not work. XOne condition which springs to mind where it doesn't work is: XYou package two files, the first one 30 blocks, the second 1 block. XLet's call the difference between 512 and the number of bytes which are Xactually used in the first file's last block "spare bytes". XIf the number of lines in the first file is greater than 512+"spare bytes", Xthen VMS_SHAR will attempt to create a filename for part 2, but it has not Xbeen properly initialised and will fail. Well, not really. It will complain Xabout an unknown symbol "I", and will not append its version suffixes, but Xwill still create the files, only using standard VMS version increments. X XAnother case which is insufficiently handled is when one single file Xexceeds MAX_PART_SIZE. The whole numbering scheme gets out of kilter Xand the packager is left with a renaming task. X XAbout Version 3.00: XI had some discussions with usenet members, particularly with XEd <MCGUIRE@GRIN2.BITNET> about trailing white space. XHe suggested to use a length count at the beginning of each line, and to Xsplit lines longer than 80 characters and restore them at the receiving site. XI didn't follow that suggestion for three reasons: X 1. The unix shar doesn't cater for this either. X 2. It would have increased processing time a lot. X 3. I didn't ever experience serious difficulties due to lost trailing X white space. X XHowever, it was Ed who came up with a method to use EDT to insert/remove Xthose leading "X"s. As this turned out to be 3 times faster than my Xprevious method (OPEN/READ/lexical function/WRITE) this was implemented Xin version 3.00 X XSome days later, after some more good arguments and examples from Ed: XOK, I'm convinced that splitting lines is a good idea. XHowever, this will take some time to implement as it needs to be done Xin TPU and I'm a complete TPU agnostic. But that's what producing these Xtoys is all about, no? Playful learning. X XThe created procedure will now have "Goto <next part>" at the end of Xeach part and "Exit" as its last statement. This should take care of Xsignatures which might occur at the end of each part. X X3.07 introduced usage of TPU to add/remove leading Xs. I'm slowly getting Xon top of TPU. X XOn Version 4.00: XAfter some further useful contributions from the net - esp. BERNIE@UMBC2.EDU X(Bernie Duffy) - a line splitting feature was introduced. I.e. if a line is Xlonger than 79 characters it gets split. However, if a line contains TABs Xthis may not be sufficient. The only way out is to compensate for TABs, Xassuming standard 8-column-TABs. However, I couldn't get TPU to search for Xa TAB within a line. So you have to de-TAB the files before you feed them Xto VMS_SHAR. I hope to be able to present a TPU-deTAB soon. X X05-Aug-1987, after some holidays (Cook Islands, great spot): XNow, thanks to X TOLLIVER%ORN.MFENET@NMFECC.ARPA (John Tolliver) and X STRASSER@RSBS0.ANU (Mike Strasser) and X MSIEWEKE@GTRI01@BITNET XI know how to search for TABs within a line, but this was of course a Xdead end street, as detabbing would mess up the checksums. XSo instead, I came up with a TPU detabbing procedure, called DETAB.COM X XOn Version 4.04 15-Sep-1987: XOh boy, am I embarassed. How could this happen? XThe TPU code checking for lines which need splitting was wrong. I should have Xnoticed much earlier when somebody from the net (???) noticed that both Xoccurrences of "79" should be changed to "78". The real problem, however, was Xthat I subtracted 1 from Length(Current_Line). Why did I do that? XAnyway, wiping off the egg from my face (until next time). X XOn Version 4.05 21-Sep-1987: XCorrected a bug in the part counting code. All previously documented Vrestrictions still apply, i.e.: due to added characters in the packaging proces Xs Xthe actual number of parts may still differ from what the procedure's first Xpass through the files determines. X XOn Version 4.06 22-Sep-1987: XAfter many useful exchanges with usenet members X[special thanks to SDCSVAX!SDCC12.UCSD.EDU!GR65 (Steve Piper) X and SLOANE@UKANVAX.BITNET (Bob Sloane) ] XI added some informational messages when the packaging process creates more Xfiles than originally anticipated. XAlso, initialising Part_Size to 4 instead of zero improved the part-counting Va lot. The rationale is that a packaged null-file will produce about 3.6 blocks X. X XOn Version 5.00 24-Sep-1987: XNow here is a good idea from Bob Sloane how to quote control characters. XIts not entirely new, but Bob's example TPU code triggered my renewed interest. XAll control characters (0...31,127...255) are translated to a 3-digit number, Xquoted by "``" (accent grave); all "``" are doubled. XSo <TAB>, <BEL>, <ESC> shouldn't be a problem any more. XAgain, TPU exhibits a deficiency: no inverse function to ASCII. X[string:=ASCII(integer); where is: integer:=FUNCTION(string) ? And why is this Xfunction called ASCII, and not CHAR?] X XSome minor changes to the remaining code were also made. And the new encoding XTPU code is heavily sprinkled with comments, which is not feasible for the Xdecoding part, because that is transmitted every time VMS_SHAR is used. X XOn Version 5.01 01-Oct-1987: XVMS_SHAR now checks for the logical name SHAR_MAX_SIZE. If that name is defined Xas a number, that number will be used as MAX_PART_SIZE. $ GoSub Convert_File $ Exit