<> <> <> <> DIRECTORY Ascii USING [CR, SP, TAB, LF, BS, ControlA, ControlX, FF, NUL, ESC, DEL, BEL], Atom USING [PropList], Rope USING [ROPE], RTBasic USING [TV, Type], System USING [GreenwichMeanTime], Time USING [Current] ; IO: CEDAR DEFINITIONS IMPORTS Time = BEGIN <> STREAM: TYPE = REF STREAMRecord; STREAMRecord: TYPE = RECORD[ streamProcs: REF StreamProcs, -- see below streamData: REF ANY, -- data private to streamProcs propList: Atom.PropList _ NIL, -- permits associating arbitrary information with stream. see StoreData, LookupData, and RemoveData below. backingStream: STREAM _ NIL -- for use with layered streams. ]; Handle: TYPE = STREAM; -- for backward compatibility ROPE: TYPE = Rope.ROPE; TV: TYPE = RTBasic.TV; Type: TYPE = RTBasic.Type; Base: TYPE = [2..36]; GreenwichMeanTime: TYPE = System.GreenwichMeanTime; <> <> CreateViewerStreams: PROC [name: ROPE] RETURNS [in: STREAM, out: STREAM]; <> <> <> <> CreateInputStreamFromRope, RIS: PROC [rope: ROPE, oldStream: STREAM _ NIL] RETURNS [stream: STREAM]; <> CreateOutputStreamToRope, ROS: PROC RETURNS [stream: STREAM]; <> <> CreateInputStreamFromText: PROC [text: REF READONLY TEXT, oldStream: STREAM _ NIL] RETURNS [stream: STREAM]; <> CreateOutputStreamToText: PROC [text: REF TEXT, oldStream: STREAM _ NIL] RETURNS [stream: STREAM]; <> << >> CreateDribbleStream: PROCEDURE [stream: STREAM, dribbleTo: STREAM, flushEveryNChars: INT _ -1] RETURNS [s: STREAM]; < 0, will automatically call Flush on dribbleTo every nChars. >> CreateBufferedOutputStream: PROC [out: STREAM, deliverWhen: DeliverWhenProc _ IsACR, howOften: INT _ 256] RETURNS [s: STREAM]; <= 0 and) howOften characters have been output since last flush, or (b) (deliverWhen # NIL and) deliverWhen[char] is TRUE, or (c) an explicit Flush is performed, at which point the characters are output to stream using PutBlock. Characters are buffered in a reftext so no allocations are performed. >> CreateFilterCommentsStream: PROCEDURE [stream: IO.STREAM] RETURNS [s: STREAM]; <> <<>> AppendStreams: PROC [in: STREAM, from: STREAM, appendToSource: BOOL _ TRUE]; <> <> noWhereStream: STREAM; <> noInputStream: IO.STREAM; <> <> <> <> CreateEditedStream: PROCEDURE [in: STREAM, echoTo: STREAM, deliverWhen: DeliverWhenProc _ IsACR] RETURNS [stream: STREAM]; <> <> <> <<>> DeliverWhenProc: TYPE = PROC[char: CHAR, stream: STREAM] RETURNS[BOOL]; IsACR: DeliverWhenProc; -- returns TRUE for CR. ChangeDeliverWhen: PROC [self: STREAM, proc: DeliverWhenProc] RETURNS [oldProc: DeliverWhenProc]; <> GetBufferContents: PROC [self: STREAM] RETURNS[buffer: ROPE]; <> <<>> Rubout: SIGNAL; <> <> <> GetChar: PROC[self: STREAM] RETURNS[CHAR] = INLINE {RETURN[self.streamProcs.getChar[self]]}; EndOf: PROC[self: STREAM] RETURNS [BOOL] = INLINE {RETURN[self.streamProcs.endOf[self]]}; <> CharsAvail: PROC[self: STREAM] RETURNS [BOOL] = INLINE {RETURN[self.streamProcs.charsAvail[self]]}; <> <> <> WaitUntilCharsAvail: PROC [stream: IO.STREAM]; <> <> GetBlock: PROC[self: STREAM, block: REF TEXT, startIndex: NAT _ 0, stopIndexPlusOne: NAT _ LAST[NAT]] RETURNS[nBytesRead: NAT] = INLINE {RETURN[self.streamProcs.getBlock[self, block, startIndex, stopIndexPlusOne]]}; <> << for those streams that provide an implementation for getBlock, e.g. file streams. Otherwise, the getBlock is exactly the same as, i.e. is accomplished by, performing n calls to GetChar. See discussion of default procedures below. >> UnsafeGetBlock: UNSAFE PROC[self: STREAM, block: UnsafeBlock] RETURNS[nBytesRead: INT] = UNCHECKED INLINE {RETURN[self.streamProcs.unsafeGetBlock[self, block]]}; <> << is defined below in the section "Private and Semi-private types." >> Backup: PROC [self: STREAM, char: CHAR]; <> <> PeekChar: PROC[self: STREAM] RETURNS [char: CHAR]; <> SetEcho: PROC[self: STREAM, echoTo: STREAM] RETURNS [oldEcho: STREAM]; <> <> PutChar: PROC[self: STREAM, char: CHAR] = INLINE {self.streamProcs.putChar[self, char]}; PutBlock: PROC[self: STREAM, block: REF READONLY TEXT, startIndex: NAT _ 0, stopIndexPlusOne: NAT _ LAST[NAT]] = INLINE {self.streamProcs.putBlock[self, block, startIndex, stopIndexPlusOne]}; UnsafePutBlock: PROC[self: STREAM, block: UnsafeBlock] = INLINE {self.streamProcs.unsafePutBlock[self, block]}; Flush: PROC [self: STREAM] = INLINE {self.streamProcs.flush[self]}; <> EraseChar: PROC[self: STREAM, char: CHAR]; <> <> CurrentPosition: PROC[self: STREAM] RETURNS[position: INT]; <> NewLine: PROC [self: STREAM] = INLINE {IF self.CurrentPosition[] # 0 THEN self.PutChar['\n]}; <> SpaceTo: PROC [self: STREAM, n: INT, nextLine: BOOLEAN _ TRUE]; <> GetOutputStreamRope: PROC [self: STREAM] RETURNS [ROPE]; <> <> Reset: PROC[self: STREAM] = INLINE {self.streamProcs.reset[self]}; <> Close: PROC[self: STREAM, abort: BOOL _ FALSE] = INLINE {self.streamProcs.close[self, abort]}; UserAbort: PROC [stream: STREAM] RETURNS [abort: BOOLEAN]; <> SetUserAbort: PROC [stream: STREAM]; <> ResetUserAbort: PROC [stream: STREAM] ; <> GetIndex: PROC[self: STREAM] RETURNS [index: INT] = INLINE {RETURN[self.streamProcs.getIndex[self]]}; SetIndex: PROC[self: STREAM, index: INT] = INLINE {self.streamProcs.setIndex[self, index]}; GetLength: PROC[self: STREAM] RETURNS [length: INT]; SetLength: PROC[self: STREAM, length: INT]; <> Put: PROC [stream: STREAM, v1, v2, v3: Value _ [null[]]]; <> << use PutF, described below, which allows specification of field Width, base, etc.>> <> <> PutF: PROC [stream: STREAM, format: ROPE _ NIL, v1, v2, v3, v4, v5: Value _ [null[]]]; <> <> <> <> <> PutList, PutL: PROC [stream: STREAM, list: LIST OF Value]; <> PutFList, PutFL: PROC [stream: STREAM, format: ROPE _ NIL, list: LIST OF Value]; PutFToRope, PutFR: PROC [format: ROPE _ NIL, v1, v2, v3, v4, v5: Value _ [null[]]] RETURNS [r: ROPE] = INLINE {stream: STREAM _ CreateOutputStreamToRope[]; stream.PutF[format, v1, v2, v3, v4, v5]; r _ stream.GetOutputStreamRope[]; stream.Close[]; }; PutText: PROC [self: STREAM, t: REF READONLY TEXT] = INLINE {self.PutBlock[t]}; <> PutRope: PROC [self: STREAM, r: ROPE]; <> PutTV: PROCEDURE[stream: STREAM, tv: TV, depth: INT _ 4, width: INT _ 32, verbose: BOOL _ FALSE]; <> PutType: PROC[stream: STREAM, type: Type, depth: INT _ 4, width: INT _ 32, verbose: BOOLEAN _ FALSE]; <> <> <> PutSignal: PROC [stream: IO.STREAM, signalTV, argsTV: TV _ NIL]; <> <> <> atom: PROC [v: ATOM] RETURNS [Value] = INLINE {RETURN[[atom[v]]]}; bool: PROC [v: BOOL] RETURNS [Value] = INLINE {RETURN[[boolean[v]]]}; card: PROC [v: LONG CARDINAL] RETURNS [Value] = INLINE {RETURN[[cardinal[v]]]}; char: PROC [v: CHAR] RETURNS [Value] = INLINE {RETURN[[character[v]]]}; int: PROC [v: INT] RETURNS [Value] = INLINE {RETURN[[integer[v]]]}; real: PROC [v: REAL] RETURNS [Value] = INLINE {RETURN[[real[v]]]}; refAny: PROC [v: REF READONLY ANY] RETURNS [Value] = INLINE {RETURN[[refAny[v]]]}; rope: PROC [v: ROPE] RETURNS [Value] = INLINE {RETURN[[rope[v]]]}; string: PROC [v: LONG STRING] RETURNS [Value] = INLINE {RETURN[[string[v]]]}; text: PROC [v: REF READONLY TEXT] RETURNS [Value] = INLINE {RETURN[[text[v]]]}; time: PROC [v: GreenwichMeanTime _ Time.Current[]] RETURNS [Value] = INLINE {RETURN[[time[v]]]}; -- i.e. time[] is the current time tv: PROC [v: RTBasic.TV] RETURNS [Value] = INLINE {RETURN[[tv[v]]]}; type: PROC [t: RTBasic.Type] RETURNS [Value] = INLINE {RETURN[[type[t]]]}; <> <> CharProc: TYPE = PROC [char: CHAR, stream: STREAM] RETURNS [quit: BOOL _ FALSE, include: BOOL _ TRUE]; <> GetSequence: PROC [stream: STREAM, charProc: CharProc] RETURNS[ROPE]; <> GetLine: PROC [stream: STREAM] RETURNS[ROPE]; <> <> <<{line: ROPE = GetSequence[stream, LineAtATime];>> <<[] _ GetChar[stream ! IO.EndOfStream => CONTINUE];>> <> <<};>> LineAtATime: CharProc; <> EveryThing: CharProc; <> <> GetCedarToken: PROC [stream: STREAM] RETURNS[ROPE]; <> <> FromTokenProc: TYPE = PROC[closure: CedarScanner.GetClosure, token: CedarScanner.Token]; GetCedarScannerToken: PRIVATE PROC [stream: STREAM, fromTokenProc: FromTokenProc]; <> <<>> <> <<{fromTokenProc: FromTokenProc = {value _ CedarScanner.ContentsFromToken[closure, token]};>> <> <<};>> <> <> <> <> SyntaxError: ERROR[stream: IO.STREAM, msg: ROPE _ NIL]; GetAtom: PROC [stream: STREAM] RETURNS [ATOM]; <> GetBool: PROC[stream: STREAM] RETURNS[BOOLEAN]; <> <> << UserExec.ExecConfirm and UserExec.ViewerConfirm provide convenient and standard ways of asking for confirmation. These procedures also post menu buttons and handle the case where the user has typed ahead before the need for confirmation arose.>> GetCard: PROC [stream: STREAM] RETURNS [LONG CARDINAL]; GetInt: PROC [stream: STREAM] RETURNS [INT]; <> GetReal: PROC [stream: STREAM] RETURNS [REAL]; GetRope: PROC [stream: STREAM] RETURNS [ROPE]; <> <<>> GetId: PROC [stream: STREAM] RETURNS [ROPE]; <> <> GetRefAny: PROC [stream: STREAM] RETURNS [REF ANY]; <> <<>> GetRefAnyLine: PROC [stream: STREAM] RETURNS[LIST OF REF ANY]; <> << e.g. if user types FOO FIE FUM{cr} returns ($FOO, $FIE, $FUM). If user types FOO FIE FUM{sp}{cr} continues reading on next line. Useful for line oriented command interpreters. (Note that a CR typed inside of a list simply has the same effect as a space, i.e. terminates the previous identifier, but not the read operation).>> <> <> <> <> <> BreakProc: TYPE = PROC [char: CHAR] RETURNS [CharClass]; CharClass: TYPE = {break, sepr, other}; GetToken: PROC [stream: STREAM, breakProc: BreakProc _ TokenProc] RETURNS[ROPE]; <> <> <> <> SkipOver: PROC [stream: STREAM, skipWhile: BreakProc _ WhiteSpace]; <> <> <> AskUser: PROC [msg: Rope.ROPE, in, out: STREAM, defaultKey: ATOM _ NIL, keyList: LIST OF ATOM _ NIL, timeout: INT _ -1] RETURNS [value: ATOM]; < LIST[$Yes, $No].>> <> <> <> <> <> <> StoreData: PROC [self: STREAM, key: ATOM, data: REF ANY]; <> AddData: PROC [self: STREAM, key: ATOM, data: REF ANY] ; <> LookupData: PROC [self: STREAM, key: ATOM] RETURNS [REF ANY]; <> RemoveData: PROC [self: STREAM, key: ATOM]; <> <> <> <> <> <> <<>> <> <<>> <> <> <> <> <> <<]; -- initialize the block of procedures>> <> <<}; -- of CreateMessageWindowStream>> <<>> <> <> <> <> <> <> <> <> <<}; >> <<>> <> <> <> <<}; >> <<>> CreateProcsStream: PROC[streamProcs: REF StreamProcs, streamData: REF ANY, backingStream: STREAM _ NIL] RETURNS[stream: STREAM]; <> <> <> <> <> CreateRefStreamProcs: PROC[ getChar: PROC[self: STREAM] RETURNS[CHAR] _ NIL, endOf: PROC[self: STREAM] RETURNS[BOOL] _ NIL, charsAvail: PROC[self: STREAM] RETURNS[BOOL] _ NIL, getBlock: PROC[self: STREAM, block: REF TEXT, startIndex: NAT, stopIndexPlusOne: NAT] RETURNS[nBytesRead: NAT] _ NIL, unsafeGetBlock: UNSAFE PROC[self: STREAM, block: UnsafeBlock] RETURNS[nBytesRead: INT] _ NIL, putChar: PROC[self: STREAM, char: CHAR] _ NIL, putBlock: PROC[self: STREAM, block: REF READONLY TEXT, startIndex: NAT, stopIndexPlusOne: NAT] _ NIL, unsafePutBlock: PROC[self: STREAM, block: UnsafeBlock] _ NIL, flush: PROC[self: STREAM] _ NIL, reset: PROC[self: STREAM] _ NIL, close: PROC[self: STREAM, abort: BOOL _ FALSE] _ NIL, getIndex: PROC[self: STREAM] RETURNS [INT] _ NIL, setIndex: PROC[self: STREAM, index: INT] _ NIL, getLength: PROC[self: STREAM] RETURNS [length: INT] _ NIL, setLength: PROC[self: STREAM, length: INT] _ NIL, backup: PROC[self: STREAM, char: CHAR] _ NIL, userAbort: PROC[self: STREAM] RETURNS[abort: BOOL] _ NIL, setUserAbort: PROC[self: STREAM] _ NIL, resetUserAbort: PROC[self: STREAM] _ NIL, setEcho: PROC[self: STREAM, echoTo: STREAM] RETURNS [oldEcho: STREAM] _ NIL, eraseChar: PROC[self: STREAM, char: CHAR] _ NIL, currentPosition: PROC[self: STREAM] RETURNS[position: INT] _ NIL, name: Rope.ROPE _ NIL -- used for printing the stream itself as a ref any. ] RETURNS [REF StreamProcs]; <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> << >> AmbushProcsStream: PRIVATE PROCEDURE[self: STREAM, streamProcs: REF StreamProcs, streamData: REF ANY, reusing: STREAM _ NIL] ; <> UnAmbushProcsStream: PRIVATE PROCEDURE[self: STREAM]; <> <> <> Implements: PRIVATE PROC [self: STREAM, operation, via: PROC ANY RETURNS ANY]; <> <> <> UncheckedImplements: -- Even more -- PRIVATE PROC [self: IO.STREAM, operation, via: PROC ANY RETURNS ANY, data: REF ANY _ NIL, procRef: REF ANY, key: ATOM]; <> <<>> LookupProc: PRIVATE PROC [self: STREAM, operation: PROC ANY RETURNS ANY] RETURNS[proc: REF ANY]; <> <> <> <> <> <<"This is an integer in a 5 position field: | 17|".>> <> <> <> <> <<"string" means a Value that is either a string, rope, text, bool, or a number. BOOLEANs passed to a "string" routine print as TRUE or FALSE and CHARACTERs print as characters.>> <<%e: accepts numbers. Like Fortran E format, prints in "scientific notation" (mantissa and exponent).>> <<%f: accepts numbers. Like Fortran F format, prints in "fixed point" format.>> <<%g: accepts all values. Prints whatever you give it sensibly. When in doubt, use %g.>> <<%h: like %g, except that control characters (code < 40B) print as "^.>> <<%l: accepts ROPEs. Changes looks for ViewerStreams, is a NOP for all other streams so that the client does not have to know whether he is printing to a viewer or a file or whatever. The value is interpreted as a sequence of looks, e.g. h.PutF["Make this %lbold%l for emphasis", rope["b"], rope["B]] will print "Make this bold for emphasis". (Note that B means remove the bold look because it is shift-b.) The character space has the same interpretation as for tioga, i.e. removes all looks.>> <<%r: accepts numbers, interprets as time interval in seconds, and prints as HH:MM:SS>> <<%t: accepts numbers and Time[], prints in usual date format with time zone (e.g. " PDT")>> <> <<%b: print in octal.>> <<%d: print in decimal.>> <<%x: print in hex.>> <> <> <> <> <> <> SetPFCodeProc: PROC [stream: STREAM, char: CHAR, codeProc: PFCodeProc]; <> PFCodeProc: TYPE = PROC [stream: STREAM, val: Value, format: Format, char: CHAR]; Format: TYPE = RECORD [form: ROPE, first: INT]; <> <> PFStarCodeProc: TYPE = PROC [stream: STREAM, char: CHAR]; SetPFStarCodeProc: PROC [stream: STREAM, char: CHAR, codeProc: PFStarCodeProc]; <> <> <<>> <> <> <> <<};>> <> <> <> <<>> <<{Viewer - class: Typescript, name: Work Area A: Executive}>> AttachRefPrintProc: PROC [refType: RTBasic.Type, refPrintProc: RefPrintProc]; <> RefPrintProc: TYPE = PROC [ref: REF READONLY ANY, stream: STREAM, depth: INT _ 4, width: INT _ 32, verbose: BOOL _ FALSE]; <> << >> <> <> <> <> <<};>> <> <<>> AttachTVPrintProc: PROC [type: RTBasic.Type, tvPrintProc: TVPrintProc, canHandleRemote: BOOL _ FALSE]; <> TVPrintProc: TYPE = PROC [tv: RTBasic.TV, stream: STREAM, depth: INT _ 4, width: INT _ 32, verbose: BOOL _ FALSE]; <> UserAborted: ERROR [abortee: REF ANY _ NIL, msg: ROPE _ NIL]; <> EndOfStream: ERROR [stream: STREAM]; <> Error: ERROR [ec: ErrorCode, stream: STREAM]; ErrorCode: TYPE = { NotImplementedForThisStream, <> IllegalBackup, <> StreamClosed, <> FileTooLong, <> BadIndex <> <> }; Signal: SIGNAL [ec: SignalCode, stream: STREAM]; SignalCode: TYPE = { NotImplementedForThisStream, <> ProcedureHasWrongType, <> NotARefType, <> UnmatchedLeftParen, UnmatchedStringDelim, <> UnprintableValue, TypeMismatch, UnknownFormat <> }; <> ValueType: TYPE = { null, atom, boolean, character, cardinal, integer, real, refAny, rope, string, text, time, tv, type}; Value: TYPE = RECORD [SELECT type: ValueType FROM null => NULL, atom => [value: ATOM], boolean => [value: BOOL], character => [value: CHAR], cardinal => [value: LONG CARDINAL], integer => [value: INT], real => [value: REAL], refAny => [value: REF READONLY ANY], rope => [value: ROPE], string => [value: LONG STRING], text => [value: REF READONLY TEXT], time => [value: GreenwichMeanTime], tv => [value: RTBasic.TV], type => [value: RTBasic.Type], ENDCASE]; UnsafeBlock: TYPE = RECORD [ base: LONG POINTER, startIndex, stopIndexPlusOne: INT --bytes--]; <> <> StreamProcs: TYPE = PRIVATE RECORD[ getChar: PROC[self: STREAM] RETURNS[CHAR] _ NIL, endOf: PROC[self: STREAM] RETURNS[BOOL] _ NIL, charsAvail: PROC[self: STREAM] RETURNS[BOOL] _ NIL, getBlock: PROC[self: STREAM, block: REF TEXT, startIndex: NAT, stopIndexPlusOne: NAT] RETURNS[nBytesRead: NAT] _ NIL, unsafeGetBlock: UNSAFE PROC[self: STREAM, block: UnsafeBlock] RETURNS[nBytesRead: INT] _ NIL, putChar: PROC[self: STREAM, char: CHAR] _ NIL, putBlock: PROC[self: STREAM, block: REF READONLY TEXT, startIndex: NAT, stopIndexPlusOne: NAT] _ NIL, unsafePutBlock: PROC[self: STREAM, block: UnsafeBlock] _ NIL, flush: PROC[self: STREAM] _ NIL, reset: PROC[self: STREAM] _ NIL, close: PROC[self: STREAM, abort: BOOL _ FALSE] _ NIL, getIndex: PROC[self: STREAM] RETURNS [INT] _ NIL, setIndex: PROC[self: STREAM, index: INT] _ NIL, otherStreamProcs: LIST OF StreamProperty _ NIL, -- used for specifying the implementation of optional, generic procedures. See Implements below. name: Rope.ROPE ]; StreamProperty: TYPE = REF StreamPropertyRecord; StreamPropertyRecord: TYPE = PRIVATE RECORD[operation, via: PROC ANY RETURNS ANY, proc: REF ANY, key: ATOM]; <> CR: CHAR = Ascii.CR; SP: CHAR = Ascii.SP; TAB: CHAR = Ascii.TAB; LF: CHAR = Ascii.LF; BS: CHAR = Ascii.BS; ControlA: CHAR = Ascii.ControlA; ControlX: CHAR = Ascii.ControlX; FF: CHAR = Ascii.FF; NUL: CHAR = Ascii.NUL; ESC: CHAR = Ascii.ESC; DEL: CHAR = Ascii.DEL; BEL: CHAR = Ascii.BEL; <> BackSlashChar: PROC [char: CHAR, stream: STREAM _ NIL] RETURNS [CHAR]; <> Zone: PRIVATE ZONE; -- prefixed zone used for storing stream data, stream procs, etc. END. Converting from 3.5 The following are some suggestions about how to convert a program using IO.GetRope or IO.GetToken to the new scheme of things. These suggestions should cover about 95% of the cases. Let us consider GetToken first. A old-style call of the form s.GetToken[], which defaults the second argument, can stay the same. But you might want to consider using GetCedarToken instead, especially if what you are doing is reading mesa: GetCedarToken will do more Cedar-like things for real numbers, quoted literals, quoted text, and the like. GetRope is only a little more complicated. An old-style call of the form s.GetRope[], which defaults the second and third arguments, can be replaced by the new call s.GetToken[IDProc]. Note that the new call s.GetToken[] would also probably do what you want, the only difference being in the treatment of break characters: for example, the old s.GetRope[] would read @foo as a single token, while the new s.GetToken[] would read it as two separate tokens. Similarly, /indigo/cedar/top/io.df would be read as 8 tokens by the new s.GetToken[], but as only one token by the old s.GetRope[] and the new s.GetToken[IDProc]. All three of these have the same effect if your desired tokens are always separated by white space. Beware! If you use GetRope, you cannot depend on compile error messages alone to tell you where changes have to be made. The new GetRope means something quite different from the old GetRope, but any call to the old GetRope that defaults the second and third arguments will still look OK to the compiler as a call on the new GetRope. Clients of the old GetRope were just parsing some random input into things separated by white space. A client of the new GetRope should be in the process of parsing the input stream as a sequence of mesa tokens. Such a client calls the new GetRope when she expects a rope literal to be the next thing in the input stream, and the call to GetRope either returns the corresponding rope or raises an error. Thus, all calls to the old GetRope must be changed to GetToken as described in the previous paragraph, and you must search your program to find them all. If you have defined your own IO.BreakProcs for use with GetToken, the following recipe should work: change all of the StopAndPutBackChar and StopAndTossChar to sepr, StopAndIncludeChar to break, and KeepGoing to other. If you have defined your own IO.BreakProcs for use with GetRope, you should change KeepGoing to other and everything else to sepr, and then use GetToken in place of GetRope. However, you might want to rewrite in terms of GetSequence instead. <<>> <<>>