############################################################################ # # File: bufread.icn # # Subject: Procedures for buffered read and lookahead # # Author: Charles A. Shartsis # # Date: March 11,1995 # ############################################################################ # # This file is in the public domain. # ############################################################################ # # Version: 1.0 # ############################################################################ # # Synopsis: # # bufopen(s) Open a file name s for buffered read and lookahead # bufread(f) Read the next line from file f # bufnext(f, n) Return the next nth record from file f # without changing the next record to be read by # bufread # bufclose(f) Close file f # ############################################################################ # # These procedures provide a mechanism for looking ahead an # arbitrary number of records in an open file while still # keeping track of the logical current record and end-of-file. # Although similar in intent to the procedures in buffer.icn, these # procedures are used differently. The procedures bufopen, # bufread, and bufclose were designed to closely mirror the # built-in open, read, and close. # # A code segment like # # file := open("name", "r") | stop("open failed") # while line := read(file) do { # ...process current line... # } # close(file) # # can be changed to the following with no difference in behavior: # # file := bufopen("name", "r") | stop("open failed") # while line := bufread(file) do { # ...process current line... # } # bufclose(file) # # However in addition to processing the current line, one may # also process subsequent lines BEFORE they are logically # read: # # file := bufopen("name", "r") | stop("open failed") # while line := bufread(file) do { # ...process current line... # line := bufnext(file,1) # return next line # ...process next line... # line := bufnext(file,2) # return 2nd next line # ...process 2nd next line... # ...etc... # } # bufclose(file) # # In the code above, calls to bufnext do not affect the results of # subsequent bufread's. The bufread procedure always steps through # the input file a line at a time without skipping lines whether or # not bufnext is called. # ############################################################################ # # Here is a more detailed description of the procedures: # # bufopen(s) # ========== # Produces a file resulting from opening s for reading ("r" option), # but fails if the file cannot be opened. if s is missing or # the value of s is &null, then standard input is opened and # &input is returned. Unlike the Icon open function, bufopen() # can and must be called prior to any call to bufread or bufnext # involving standard input. Unlike named files, only one buffered # standard input may be open at any given time. # # Default: # s &null (indicates &input should be opened for buffered # reading) # # Errors (from open): # 103 s not string # # Errors (new): # Attempt to open standard input when currently open # # # bufread(f) # ========== # Produces a string consisting of the next line from f, but fails on # end of file. Calls to bufnext do not affect the results of # subsequent bufread's. The procedure bufread always steps # through a file a line at a time without skipping lines. The # procedure bufread fails when a logical end of file is # reached, i.e., when the physical end of file has # been reached AND the internal buffer is empty. # # Default: # f &input # # Errors: # f is not a file # f not opened for buffered reads (includes &input) # # # bufnext(f, n) # ============= # Produces a string consisting of the nth next line from f after # the current line. It fails when the physical end of file # has been reached. # # Default: # f &input # n 1 (the next line after the current one) # # Errors: # f is not a file # f not opened for buffered reads (includes &input) # n not convertible to integer # n not positive # # # bufclose(f) # =========== # Produces f after closing it. Standard input must # be closed before it can be reopened using bufopen. # If standard input is closed, all lines read using bufnext # are lost when it is reopened. In general, there is no # practical reason to bufclose and then bufopen standard input. # One may want to bufclose standard input to release its # internal buffer for garbage collection. # # Default: # f &input # # Errors (from close): # 105 f not file # ############################################################################ global __buf procedure bufopen(fname) local file if /__buf then __buf := table(&null) if /fname then { /__buf[&input] | stop("bufopen: Standard input is already open") __buf[&input] := [] return &input } else if file := open(fname, "r") then { __buf[file] := [] return file } else fail end procedure bufclose(file) if /__buf then __buf := table(&null) if /file then { __buf[&input] := &null return &input } else { close(file) __buf[file] := &null return file } end procedure bufread(file) local buf if /__buf then __buf := table(&null) if /file then file := &input type(file) == "file" | stop("bufread: Parameter is not a file") buf := \__buf[file] | stop("bufread: File not open for buffered reads") return get(buf) | read(file) end procedure bufnext(file, n) local buf if /__buf then __buf := table(&null) if /file then file := &input if /n then n := 1 type(file) == "file" | stop("bufnext: Parameter is not a file") integer(n) | stop("bufnext: Look ahead count was not convertible to integer") (n > 0) | stop("bufnext: Look ahead count was non-positive") buf := \__buf[file] | stop("bufnext: File not open for buffered reads") return buf[n] | ( while *buf < n do (put(buf, read(file)) | break &fail) ) | buf[n] end