summaryrefslogtreecommitdiff
path: root/ipl/procs/bufread.icn
blob: 63102897109d349f748883e1dab3b29d0b103a0d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
############################################################################
#
#   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