File: more-port.scm

package info (click to toggle)
scsh-0.6 0.6.7-3
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 15,124 kB
  • ctags: 16,788
  • sloc: lisp: 82,839; ansic: 23,112; sh: 3,116; makefile: 829
file content (341 lines) | stat: -rw-r--r-- 10,796 bytes parent folder | download | duplicates (5)
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
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
; Copyright (c) 1993-1999 by Richard Kelsey and Jonathan Rees. See file COPYING.

; Additional port types

;----------------
; Ports which keep track of the current row and column.
;
; When the row or column data is requested we need to process the characters
; between the port's current index and the index at the time of the previous
; check.
;
; When a buffer operation is requested we need to process any remaining
; characters in the old buffer.  If the operation is a block read or write
; we also need to process whatever is read or written.
;
; port:        the tracking port - needed for its buffer and indicies
; sub-port:    port being tracked
; index:       the index of the next character to be processed
; row, column: position of the character at BUFFER[INDEX - 1] 

(define-record-type port-location :port-location
  (really-make-port-location sub-port index row column)
  port-location?
  (port     port-location-port set-port-location-port!) ; setter for circularity
  (sub-port port-location-sub-port)
  (index    port-location-index  set-port-location-index!)
  (row      port-location-row    set-port-location-row!)
  (column   port-location-column set-port-location-column!))

(define (make-port-location sub-port)
  (really-make-port-location sub-port (port-index sub-port) 0 0))

; Update the data and return what you get.

(define (current-row port)
  (let ((data (port-data port)))
    (if (port-location? data)
	(begin
	  (obtain-port-lock port)
	  (update-row-and-column! data)
	  (let ((row (port-location-row data)))
	    (release-port-lock port)
	    row))
	#f)))

(define (current-column port)
  (let ((data (port-data port)))
    (if (port-location? data)
	(begin
	  (obtain-port-lock port)
	  (update-row-and-column! data)
	  (let ((column (port-location-column data)))
	    (release-port-lock port)
	    column))
	#f)))

; Bring LOCATION up to date.

(define (update-row-and-column! location)
  (let ((at (port-index (port-location-port location)))
	(checked-to (port-location-index location))
	(buffer (port-buffer (port-location-port location))))
    (if (< checked-to at)
	(begin
	  (update-row-and-column-from-bytes! buffer checked-to at location)
	  (set-port-location-index! location at)))))

; Two nearly identical procedures to deal with code-vectors and strings.

(define (update-row-and-column-from-bytes! code-vec start end location)
  (let loop ((i start)
	     (row (port-location-row location))
	     (column (port-location-column location)))
    (cond ((= i end)
	   (set-port-location-row! location row)
	   (set-port-location-column! location column))
	  ((= (char->ascii #\newline) (code-vector-ref code-vec i))
	   (loop (+ i 1) (+ row 1) 0))
	  (else
	   (loop (+ i 1) row (+ column 1))))))

(define (update-row-and-column-from-chars! string start count location)
  (let loop ((i start)
	     (row (port-location-row location))
	     (column (port-location-column location)))
    (cond ((= i (+ start count))
	   (set-port-location-row! location row)
	   (set-port-location-column! location column))
	  ((char=? #\newline (string-ref string i))
	   (loop (+ i 1) (+ row 1) 0))
	  (else
	   (loop (+ i 1) row (+ column 1))))))

;----------------
; Input ports that keep track of the current row and column.

(define tracking-input-port-handler
  (make-buffered-input-port-handler
   (lambda (location)
     (list 'tracking-port (port-location-sub-port location)))
   (lambda (location)  ; nothing to do
     (values))
   (lambda (location buffer start needed)
     (update-row-and-column! location)  ; finish off old buffer
     (let ((res (read-block buffer start needed
			    (port-location-sub-port location))))
       (cond ((eof-object? res))
	     ((eq? buffer (port-buffer (port-location-port location)))
	      (set-port-location-index! location 0))
	     ((code-vector? buffer)
	      (update-row-and-column-from-bytes! buffer start res location))
	     (else
	      (update-row-and-column-from-chars! buffer start res location)))
       res))
   (lambda (port)
     (char-ready? (port-location-sub-port (port-data port))))))

(define (make-tracking-input-port port)
  (if (input-port? port)
      (let ((new-port 
	     (make-buffered-input-port tracking-input-port-handler
				       (make-port-location port)
				       (make-code-vector default-buffer-size 0)
				       0
				       0)))
	; make the circular link
	(set-port-location-port! (port-data new-port) new-port)
	new-port)
      (call-error "not an input port" make-tracking-input-port port)))

;----------------
; Output ports that keep track of the current row and column.

(define tracking-output-port-handler
  (make-buffered-output-port-handler
   (lambda (location)
     (list 'tracking-port (port-location-sub-port location)))
   ; flush the buffer when closing
   (lambda (location)
     (let ((port (port-location-port location)))
       (if (< 0 (port-index port))
	   (write-block (port-buffer port) 0 (port-index port)
			(port-location-sub-port location)))))
   ; Finish off the old buffer, send the characters to the child port, and
   ; then update the row and column if necessary.
   (lambda (location buffer start count)
     (update-row-and-column! location)
     (write-block buffer start count (port-location-sub-port location))
     (cond ((eq? buffer (port-buffer (port-location-port location)))
	    (set-port-location-index! location 0))
	   ((code-vector? buffer)
	    (update-row-and-column-from-bytes! buffer start count location))
	   (else
	    (update-row-and-column-from-chars! buffer start count location))))
   (lambda (port)
     (output-port-ready? (port-location-sub-port (port-data port))))))

(define (make-tracking-output-port port)
  (if (output-port? port)
      (let ((new-port (make-output-port tracking-output-port-handler
					(make-port-location port)
					(make-code-vector default-buffer-size 0)
					0
					default-buffer-size)))
	; make the circular link
	(set-port-location-port! (port-data new-port) new-port)
	new-port)
      (call-error "not an output port" make-tracking-output-port port)))

(define (fresh-line port)
  (let ((column (current-column port)))
    (if (and column (< 0 column))
	(newline port))))

;----------------
; String input ports

; All the work is done by the port code.

(define string-input-port-handler
  (make-buffered-input-port-handler
   (lambda (ignore)
     (list 'string-input-port))
   (lambda (ignore)
     (if #f #f))
   (lambda (ignore buffer start needed)
     (eof-object))
   (lambda (port) #f)))

(define (make-string-input-port string)
  (let ((buffer (make-code-vector (string-length string) 0)))
    (copy-bytes! string 0 buffer 0 (string-length string))
    (make-buffered-input-port string-input-port-handler
			      #f                 ; no additional state needed
			      buffer
			      0
		     (string-length string))))   ; number of bytes available

(define copy-bytes! (structure-ref primitives copy-bytes!))

;----------------
; String output ports

; The data field of the port is a list of (<buffer> . <char-count>) pairs
; (the car is the port itself).  When the output is wanted the buffers are
; concatenated together to get the final string.

(define buffer-size 1024)

; Concatenates all of the buffers into single string.

(define (string-output-port-output port)
  (let* ((full (cdr (port-data port)))
	 (last (port-buffer port))
	 (index (port-index port))
	 (count (apply + index (map cdr full)))  ; Scheme is a trip
	 (out (make-string count)))
    (let loop ((full (reverse full)) (i 0))
      (if (null? full)
	  (copy-bytes! last 0 out i index)
	  (let ((buffer (caar full))
		(count (cdar full)))
	    (copy-bytes! buffer 0 out i count)
	    (loop (cdr full) (+ i count)))))
    out))

(define string-output-port-handler
  (make-buffered-output-port-handler
   (lambda (port)
     '(string-output-port))
   (lambda (port)
     (values))
   (lambda (data thing start count)
     (let ((port (car data)))
       (set-cdr! (port-data port)
		 (cons (cons (full-buffer port thing start count)
			     count)
		       (cdr (port-data port))))))
   (lambda (port) #f)))

(define (full-buffer port thing start count)
  (cond ((eq? thing (port-buffer port))
	 (set-port-buffer! port (make-code-vector default-buffer-size 0))
	 thing)
	(else
	 (let ((b (make-code-vector count 0)))
	   (copy-bytes! thing start b 0 count)
	   b))))

(define (make-string-output-port)
  (let ((port (make-buffered-output-port 
	       string-output-port-handler
	       (list #f)
	       (make-code-vector default-buffer-size 0)
	       0
	       default-buffer-size)))
    (set-car! (port-data port) port)
    port))

(define (call-with-string-output-port proc)
  (let ((port (make-string-output-port)))
    (proc port)
    (string-output-port-output port)))

;----------------
; Output ports from single character consumers

(define char-sink-output-port-handler
  (make-port-handler
   (lambda (proc)
     (list 'char-sink-output-port))
   (lambda (proc)
     (values))
   (lambda (proc char)
     (proc char))
   (lambda (port) #t)))

(define (char-sink->output-port proc)
  (make-unbuffered-output-port char-sink-output-port-handler
			       proc))

; Call PROC on a port that will transfer COUNT characters to PORT and
; then quit.

(define (write-one-line port count proc)
  (call-with-current-continuation
    (lambda (quit)
      (proc (char-sink->output-port
	     (lambda (char)
	       (write-char char port)
	       (set! count (- count 1))
	       (if (<= count 0)
		   (quit #f))))))))

;----------------
; Input ports from single character producers
; The producer is passed #T if a character is needed and #F if not.
; If #F is passed and no character is ready, then #F is returned.

(define char-source-input-port-handler
  (make-port-handler
   (lambda (proc)
     (list 'char-source-input-port))
   (lambda (proc)			; nothing to do
     (values))
   (lambda (proc buffer start needed)
     (if (integer? needed)
	 (let loop ((got 0))
	   (if (= got needed)
	       got
	       (let ((next (proc #t)))
		 (cond ((char? next)
			(buffer-set! buffer (+ start got) next)
			(loop (+ got 1)))
		       ((= got 0)
			(eof-object))
		       (else
			got)))))
	 (let ((next (proc (eq? needed 'any))))
	   (cond ((not next)
		  0)
		 ((eof-object? next)
		  (eof-object))
		 (else
		  (buffer-set! buffer start next)
		  1)))))
   (lambda (port)
     (error "Peek on char-source is not implemented. Complain to the scsh implementors"))))
             

(define (buffer-set! buffer index char)
  (if (string? buffer)
      (string-set! buffer index char)
      (code-vector-set! buffer index (char->ascii char))))

(define (char-source->input-port proc)
  (make-input-port char-source-input-port-handler
		   proc
		   (make-code-vector 1 0)
		   0
		   0))