-
-
Notifications
You must be signed in to change notification settings - Fork 88
Expand file tree
/
Copy pathport.c
More file actions
1961 lines (1770 loc) · 66.1 KB
/
port.c
File metadata and controls
1961 lines (1770 loc) · 66.1 KB
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
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* port.c - port implementation
*
* Copyright (c) 2000-2017 Shiro Kawai <shiro@acm.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the authors nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define LIBGAUCHE_BODY
#include "gauche.h"
#include "gauche/class.h"
#include "gauche/priv/portP.h"
#include "gauche/priv/builtin-syms.h"
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#undef MAX
#undef MIN
#define MAX(a, b) ((a)>(b)? (a) : (b))
#define MIN(a, b) ((a)<(b)? (a) : (b))
#define SCM_PORT_BUFFER_MODE(obj) \
(SCM_PORT(obj)->src.buf.mode & SCM_PORT_BUFFER_MODE_MASK)
#define SCM_PORT_BUFFER_SIGPIPE_SENSITIVE_P(obj) \
(SCM_PORT(obj)->src.buf.mode & SCM_PORT_BUFFER_SIGPIPE_SENSITIVE)
/* Parameter location for the global reader lexical mode, from which
ports inherit. */
static ScmParameterLoc readerLexicalMode;
/*================================================================
* Class stuff
*/
static void port_print(ScmObj obj, ScmPort *port, ScmWriteContext *ctx);
static void port_finalize(ScmObj obj, void* data);
static void register_buffered_port(ScmPort *port);
static void unregister_buffered_port(ScmPort *port);
static void bufport_flush(ScmPort*, int, int);
static void file_closer(ScmPort *p);
static int file_buffered_port_p(ScmPort *p); /* for Scm_PortFdDup */
static void file_buffered_port_set_fd(ScmPort *p, int fd); /* ditto */
static ScmObj get_port_name(ScmPort *port)
{
return Scm_PortName(port);
}
static ScmObj get_port_current_line(ScmPort *port)
{
return SCM_MAKE_INT(Scm_PortLine(port));
}
static ScmObj get_port_buffering(ScmPort *port)
{
return Scm_GetPortBufferingModeAsKeyword(port);
}
static void set_port_buffering(ScmPort *port, ScmObj val)
{
if (SCM_PORT_TYPE(port) != SCM_PORT_FILE) {
Scm_Error("can't set buffering mode to non-buffered port: %S", port);
}
Scm_SetPortBufferingMode(port,Scm_BufferingMode(val,port->direction,-1));
}
static ScmObj get_port_sigpipe_sensitive(ScmPort *port)
{
return SCM_MAKE_BOOL(Scm_GetPortBufferSigpipeSensitive(port));
}
static void set_port_sigpipe_sensitive(ScmPort *port, ScmObj val)
{
Scm_SetPortBufferSigpipeSensitive(port, SCM_BOOL_VALUE(val));
}
static ScmClassStaticSlotSpec port_slots[] = {
SCM_CLASS_SLOT_SPEC("name", get_port_name, NULL),
SCM_CLASS_SLOT_SPEC("buffering", get_port_buffering,
set_port_buffering),
SCM_CLASS_SLOT_SPEC("sigpipe-sensitive?", get_port_sigpipe_sensitive,
set_port_sigpipe_sensitive),
SCM_CLASS_SLOT_SPEC("current-line", get_port_current_line, NULL),
SCM_CLASS_SLOT_SPEC_END()
};
SCM_DEFINE_BASE_CLASS(Scm_PortClass,
ScmPort, /* instance type */
port_print, NULL, NULL, NULL, NULL);
static ScmClass *port_cpl[] = {
SCM_CLASS_STATIC_PTR(Scm_PortClass),
SCM_CLASS_STATIC_PTR(Scm_TopClass),
NULL
};
SCM_DEFINE_BASE_CLASS(Scm_CodingAwarePortClass,
ScmPort, /* instance type */
port_print, NULL, NULL, NULL, port_cpl);
/*================================================================
* Common
*/
/* Cleaning up:
* The underlying file descriptor/stream may be closed when the port
* is explicitly closed by close-port, or implicitly destroyed by the
* garbage collector. To keep consistency, Scheme ports should never
* share the same file descriptor. However, C code and Scheme port
* may share the same file descriptor for efficiency (e.g. stdios).
* In such cases, it is C code's responsibility to destroy the port.
*/
static void port_cleanup(ScmPort *port)
{
if (SCM_PORT_CLOSED_P(port)) return;
/* NB: Flush or close subroutine may raise an error and leave the port
not fully cleaned up. For now, we leave the port 'non-closed' state,
so this part may be called again---it's up to the close routine to
handle the situation gracefully.
*/
switch (SCM_PORT_TYPE(port)) {
case SCM_PORT_FILE:
if (SCM_PORT_DIR(port) == SCM_PORT_OUTPUT) {
if (!SCM_PORT_ERROR_OCCURRED_P(port)) {
bufport_flush(port, 0, TRUE);
}
if (!(SCM_PORT_FLAGS(port) & SCM_PORT_TRANSIENT)) {
unregister_buffered_port(port);
}
}
if (port->ownerp && port->src.buf.closer) port->src.buf.closer(port);
break;
case SCM_PORT_PROC:
if (port->src.vt.Close) port->src.vt.Close(port);
break;
default:
break;
}
(void)SCM_INTERNAL_FASTLOCK_DESTROY(port->lock);
SCM_PORT_CLOSED_P(port) = TRUE;
/* avoid unnecessary finalization */
Scm_UnregisterFinalizer(SCM_OBJ(port));
}
/* called by GC */
static void port_finalize(ScmObj obj, void* data)
{
port_cleanup(SCM_PORT(obj));
}
/*
* Internal Constructor.
* If this port owns the underlying file descriptor/stream,
* ownerp must be TRUE.
*/
static ScmPort *make_port(ScmClass *klass, int dir, int type)
{
ScmPort *port = SCM_NEW_INSTANCE(ScmPort, klass);
port->direction = dir & SCM_PORT_IOMASK;
port->type = type;
port->scrcnt = 0;
port->ungotten = SCM_CHAR_INVALID;
port->closed = FALSE;
port->error = FALSE;
port->ownerp = FALSE;
port->flags =
SCM_VM_RUNTIME_FLAG_IS_SET(Scm_VM(), SCM_CASE_FOLD)
? SCM_PORT_CASE_FOLD
: 0;
port->name = SCM_FALSE;
(void)SCM_INTERNAL_FASTLOCK_INIT(port->lock);
port->lockOwner = NULL;
port->lockCount = 0;
port->writeState = NULL;
port->attrs = SCM_NIL;
port->line = 1;
Scm_RegisterFinalizer(SCM_OBJ(port), port_finalize, NULL);
/* Default reader lexical mode */
Scm_PortAttrSetUnsafe(port, SCM_SYM_READER_LEXICAL_MODE,
Scm_ReaderLexicalMode());
return port;
}
/*
* Close
*/
void Scm_ClosePort(ScmPort *port)
{
ScmVM *vm = Scm_VM();
PORT_LOCK(port, vm);
PORT_SAFE_CALL(port,
do {
if (!SCM_PORT_CLOSED_P(port)) {
port_cleanup(port);
}
} while (0), /*no cleanup*/);
PORT_UNLOCK(port);
}
/*===============================================================
* Locking ports
*/
/* OBSOLETED */
/* C routines can use PORT_SAFE_CALL, so we reimplemented this in libio.scm.
Kept here for ABI compatibility; will be gone by 1.0. */
ScmObj Scm_VMWithPortLocking(ScmPort *port, ScmObj closure)
{
static ScmObj with_port_locking_proc = SCM_UNDEFINED;
SCM_BIND_PROC(with_port_locking_proc, "with-port-locking",
Scm_GaucheModule());
return Scm_ApplyRec1(with_port_locking_proc, closure);
}
/*===============================================================
* Getting information
* NB: Port attribute access API is in portapi.c
*/
ScmObj Scm_PortName(ScmPort *port)
{
return port->name;
}
int Scm_PortLine(ScmPort *port)
{
return port->line;
}
static void port_print(ScmObj obj, ScmPort *port, ScmWriteContext *ctx)
{
Scm_Printf(port, "#<%s%sport%s %A %p>",
(SCM_PORT_DIR(obj)&SCM_PORT_INPUT)? "i" : "",
(SCM_PORT_DIR(obj)&SCM_PORT_OUTPUT)? "o" : "",
SCM_PORT_CLOSED_P(obj)? "(closed)" : "",
Scm_PortName(SCM_PORT(obj)),
obj);
}
/* Returns port's associated file descriptor number, if any.
Returns -1 otherwise. */
int Scm_PortFileNo(ScmPort *port)
{
if (SCM_PORT_TYPE(port) == SCM_PORT_FILE) {
if (port->src.buf.filenum) return port->src.buf.filenum(port);
else return -1;
} else {
/* TODO: proc port */
return -1;
}
}
/* Duplicates the file descriptor of the source port, and set it to
the destination port. Both source and destination port must be
file ports.
DST also must be a file buffered port, for we rewrite the fd slot
in its private data structure. */
void Scm_PortFdDup(ScmPort *dst, ScmPort *src)
{
int r;
if (SCM_PORT_TYPE(dst) != SCM_PORT_FILE)
Scm_Error("file port required, but got %S", dst);
if (SCM_PORT_TYPE(src) != SCM_PORT_FILE)
Scm_Error("file port required, but got %S", src);
if (src->direction != dst->direction)
Scm_Error("port direction mismatch: got %S and %S",
src, dst);
int srcfd = Scm_PortFileNo(src);
int dstfd = Scm_PortFileNo(dst);
if (srcfd < 0) Scm_Error("port isn't associated to fd: %S", src);
if (dstfd < 0) Scm_Error("port isn't associated to fd: %S", dst);
if (!file_buffered_port_p(dst)) {
Scm_Error("port isn't directly associated to file: %S", dst);
}
if (dst->direction == SCM_PORT_INPUT) {
/* discard the current buffer */
ScmVM *vm = Scm_VM();
PORT_LOCK(dst, vm);
dst->src.buf.current = dst->src.buf.buffer;
dst->src.buf.end = dst->src.buf.buffer;
PORT_UNLOCK(dst);
} else {
/* flush the current buffer */
Scm_Flush(dst);
}
#if defined(GAUCHE_WINDOWS)
SCM_SYSCALL(r, _dup2(srcfd, dstfd));
#else /*!GAUCHE_WINDOWS*/
SCM_SYSCALL(r, dup2(srcfd, dstfd));
#endif /*!GAUCHE_WINDOWS*/
if (r < 0) Scm_SysError("dup2 failed");
file_buffered_port_set_fd(dst, r);
}
/* Low-level function to find if the file descriptor is ready or not.
DIR specifies SCM_PORT_INPUT or SCM_PORT_OUTPUT.
If the system doesn't have select(), this function returns
SCM_FD_UNKNOWN. */
int Scm_FdReady(int fd, int dir)
{
#if defined(HAVE_SELECT) && !defined(GAUCHE_WINDOWS)
fd_set fds;
int r;
struct timeval tm;
/* In case if this is called on non-file ports.*/
if (fd < 0) return SCM_FD_READY;
if (fd >= FD_SETSIZE) Scm_Error("Scm_FdReady: fd out of range: %d", fd);
FD_ZERO(&fds);
FD_SET(fd, &fds);
tm.tv_sec = tm.tv_usec = 0;
if (dir == SCM_PORT_OUTPUT) {
SCM_SYSCALL(r, select(fd+1, NULL, &fds, NULL, &tm));
} else {
SCM_SYSCALL(r, select(fd+1, &fds, NULL, NULL, &tm));
}
if (r < 0) Scm_SysError("select failed");
if (r > 0) return SCM_FD_READY;
else return SCM_FD_WOULDBLOCK;
#elif defined(GAUCHE_WINDOWS)
/* Windows have select(), but it can only be used on sockets.*/
if (dir == SCM_PORT_OUTPUT) {
/* We assume it is always ok */
return SCM_FD_READY;
} else {
HANDLE h = (HANDLE)_get_osfhandle(fd);
if (h == INVALID_HANDLE_VALUE) return SCM_FD_READY;
/* pipe */
DWORD avail;
if (PeekNamedPipe(h, NULL, 0, NULL, &avail, NULL) != 0) {
if (avail == 0) return SCM_FD_WOULDBLOCK;
else return SCM_FD_READY;
}
/* socket */
int optval;
int optlen;
optlen = sizeof(optval);
if (getsockopt((SOCKET)h, SOL_SOCKET, SO_TYPE, (char*)&optval, &optlen) != SOCKET_ERROR) {
fd_set fds;
int r;
struct timeval tm;
FD_ZERO(&fds);
FD_SET((SOCKET)h, &fds);
tm.tv_sec = tm.tv_usec = 0;
/* NB: The first argument of select() is ignored on Windows */
SCM_SYSCALL(r, select(0, &fds, NULL, NULL, &tm));
if (r < 0) Scm_SysError("select failed");
if (r > 0) return SCM_FD_READY;
else return SCM_FD_WOULDBLOCK;
}
/* other */
return SCM_FD_UNKNOWN;
}
#else /*!HAVE_SELECT && !GAUCHE_WINDOWS */
return SCM_FD_UNKNOWN;
#endif /*!HAVE_SELECT && !GAUCHE_WINDOWS */
}
/*===============================================================
* buffered Port
* - mainly used for buffered file I/O, but can also be used
* for other purpose, like character-code conversion port.
*/
/* [Buffered port protocol]
*
* Legends
* b = port->src.buf.buffer
* c = port->src.buf.current
* e = port->src.buf.end
* '*' = valid data
* '-' = invalid data
*
* Output
*
* When used as output, the end pointer always points one byte past
* the buffer. Initially the buffer is empty and the current pointer
* is the same as the beginning of the buffer.
*
* port->src.buf.flusher(ScmPort* p, int cnt, int forcep) is called when
* the port needs to create some room in the buffer. When the flusher
* is called, the buffer is like this:
*
* <--------------- size ---------------->
* |*********************************-----|
* ^ ^ ^
* b c e
*
* The flusher is supposed to output the cnt bytes of data beginning from
* the buffer, which is usually up to the current pointer (but the flusher
* doesn't need to check the current pointer; it is taken care of by the
* caller of the flusher).
*
* If the third argument forcep is false, the flusher may return before
* entire data is output, in case like underlying device is busy.
* The flusher must output at least one byte even in that case.
* On the other hand, if the forcep argument is true, the flusher must
* write cnt bytes; if it is not possible, the flusher must return -1 to
* indicate an error(*1).
*
* The flusher returns the number of bytes actually written out.
* If an error occurs, the flusher must return -1.
*
* The flusher must be aware that the port p is locked by the current
* thread when called.
*
* The flusher shouldn't change the buffer's internal state.
*
* After the flusher returns, bufport_flush shifts the unflushed data
* (if any), so the buffer becomes like this:
*
* <--------------- size ---------------->
* |****----------------------------------|
* ^ ^ ^
* b c e
*
* (*1) Why should these two modes need to be distinguished? Suppose
* you implement a buffered port that does character encoding conversion.
* The flusher converts the content of the buffer to different character
* encoding and feed it to some specified port. It is often the case
* that you find a few bytes at the end of the buffer which you can't
* convert into a whole character but have to wait for next byte(s).
* It is valid that you leave them in the buffer if you can expect
* more data to come. However, if you know it is really the end of
* the stream, you can't leave any data in the buffer and you should
* take appropriate action, for example, raising an error.
*
* Input
*
* When used as input, the end pointer points to one byte past the
* end of the valid data, which may be before the end of the buffer.
*
* port->src.buf.filler(ScmPort *p, int cnt) is called when the buffer
* doesn't have enough data to read. Suppose the input routine detects
* the buffer doesn't have enough data when it looks like this:
*
* <--------------- size ---------------->
* |-----------------------------****-----|
* ^ ^ ^
* b c e
*
* First, bufport_fill shifts the unread data (if any) to the beginning
* of the buffer, so it becomes like this:
*
* <--------------- size ---------------->
* |****----------------------------------|
* ^ ^
* bc e
*
* Then port->src.buf.filler is called. It is supposed to read as many
* bytes as cnt, putting them after the end pointer. The filler doesn't
* need to modify the end pointer; it is taken care of after the filler
* returns.
*
* The filler may read less than cnt bytes if all bytes of data is not
* available immediately. The filler returns the number of bytes
* actually read in. The filler should return 0 if it reaches the end
* of the data source. If an error occurs, the filler must return -1.
*
* bufport_fill then adjust the end pointer, so the buffer becomes like
* this.
*
* <--------------- size ---------------->
* |************************************--|
* ^ ^
* bc e
*
* Close
* Port is closed either explicitly (via close-port etc) or implicity
* (via GC -> finalizer). In either case, the flusher is called first
* if there's any data remaining in the buffer. Then, if the closer
* procedure (port->src.buf.closer) is not NULL, and port->owner is TRUE,
* the closer procedure is called which has to take care of any system-
* level cleanup. The closer can assume the buffer is already flushed.
*
* Ready
* When char-ready? is called on a buffered port, it first checks if
* there's any data available in the buffer. If so, it returns true.
* If not, it calls port->src.buf.ready if it is not NULL to query
* the character is ready. If port->src.buf.ready is NULL, bufport
* assumes the input is always ready.
* port->src.buf.ready should return either SCM_FD_READY, SCM_FD_WOULDBLOCK
* or SCM_FD_UNKNOWN.
*
* Filenum
* Port->src.buf.filenum is a query procedure that should return the
* underlying integer file descriptor of the port, or -1 if there's
* no associated one. If it is NULL, the port is assumed not to
* be associated to any file descriptor.
*
* Buffering mode
* {For Output}
* SCM_PORT_BUFFER_FULL : Full buffering. The buffer is flushed
* only when the buffer gets full, explicitly requested, or
* closed. This is the default, and suitable for file I/O.
*
* SCM_PORT_BUFFER_LINE : Line buffering. The buffer is flushed
* when a newline character is put, other than the normal
* circumstances as in SCM_PORT_BUFFER_FULL. Unlike C stdio,
* the buffer isn't flushed when an input is called on the same
* terminal device.
* This is natural for output of interactive communication.
* This is the default of stdout.
*
* SCM_PORT_BUFFER_NONE : data is always passed to the flusher
* procedure. The buffer is used just as a temporary storage.
* This slows down port operation significantly. Should only
* be used when you want to guarantee what you write is always
* passed to the lower layer. This is the default of stderr.
*
* {For Input}
* SCM_PORT_BUFFER_FULL : Full buffering. The filler procedure
* is called only if the buffer doesn't have enough data to
* satisfy the read request. Read-block or read-string won't
* return until the specified bytes/characters are read from
* the port, except the port reaches EOF.
*
* SCM_PORT_BUFFER_LINE : For input ports, this is almost the same
* as BUFFER_FULL, except that read-block and read-string may
* return shorter data than requested, if only that amount of
* data is immediately available. Usually this mode is suitable
* for the ports that is attached to a pipe or network.
*
* SCM_PORT_BUFFER_NONE : No buffering. Every time the data is
* requested, the filler procedure is called with exact amount
* of the requested data.
*/
#define SCM_PORT_DEFAULT_BUFSIZ 8192
ScmObj Scm_MakeBufferedPort(ScmClass *klass,
ScmObj name,
int dir, /* direction */
int ownerp, /* owner flag*/
ScmPortBuffer *bufrec)
{
int size = bufrec->size;
char *buf = bufrec->buffer;
if (size <= 0) size = SCM_PORT_DEFAULT_BUFSIZ;
if (buf == NULL) buf = SCM_NEW_ATOMIC2(char*, size);
ScmPort *p = make_port(klass, dir, SCM_PORT_FILE);
p->name = name;
p->ownerp = ownerp;
p->src.buf.buffer = buf;
if ((dir & SCM_PORT_IOMASK) == SCM_PORT_INPUT) {
p->src.buf.current = p->src.buf.buffer;
p->src.buf.end = p->src.buf.buffer;
} else {
p->src.buf.current = p->src.buf.buffer;
p->src.buf.end = p->src.buf.buffer + size;
}
if (dir == SCM_PORT_OUTPUT_TRANSIENT) {
SCM_PORT_FLAGS(p) |= SCM_PORT_TRANSIENT;
}
p->src.buf.size = size;
p->src.buf.mode = bufrec->mode;
p->src.buf.filler = bufrec->filler;
p->src.buf.flusher = bufrec->flusher;
p->src.buf.closer = bufrec->closer;
p->src.buf.ready = bufrec->ready;
p->src.buf.filenum = bufrec->filenum;
p->src.buf.seeker = bufrec->seeker;
p->src.buf.data = bufrec->data;
/* NB: DIR may be SCM_PORT_OUTPUT_TRANSIENT; in that case we don't
register the buffer. */
if (dir == SCM_PORT_OUTPUT) register_buffered_port(p);
return SCM_OBJ(p);
}
/* some accessor APIs */
int Scm_GetPortBufferingMode(ScmPort *port)
{
return SCM_PORT_BUFFER_MODE(port);
}
void Scm_SetPortBufferingMode(ScmPort *port, int mode)
{
port->src.buf.mode =
(port->src.buf.mode & ~SCM_PORT_BUFFER_MODE_MASK)
| (mode & SCM_PORT_BUFFER_MODE_MASK);
}
int Scm_GetPortBufferSigpipeSensitive(ScmPort *port)
{
return (SCM_PORT_BUFFER_SIGPIPE_SENSITIVE_P(port) != FALSE);
}
void Scm_SetPortBufferSigpipeSensitive(ScmPort *port, int sensitive)
{
if (sensitive) {
port->src.buf.mode |= SCM_PORT_BUFFER_SIGPIPE_SENSITIVE;
} else {
port->src.buf.mode &= ~SCM_PORT_BUFFER_SIGPIPE_SENSITIVE;
}
}
/* Port case folding mode is usually set at port creation, according
to the VM's case folding mode. In rare occasion we need to switch
it (but it's not generally recommended). */
int Scm_GetPortCaseFolding(ScmPort *port)
{
return (SCM_PORT_CASE_FOLDING(port) != FALSE);
}
void Scm_SetPortCaseFolding(ScmPort *port, int folding)
{
if (folding) {
SCM_PORT_FLAGS(port) |= SCM_PORT_CASE_FOLD;
} else {
SCM_PORT_FLAGS(port) &= ~SCM_PORT_CASE_FOLD;
}
}
/* Port's reader lexical mode is set at port creation, taken from
readerLexicalMode parameter. It may be altered by reader directive
such as #!r7rs.
The possible value is the same as the global reader lexical mode,
i.e. one of the symbols legacy, warn-legacy, permissive or strict-r7.
*/
ScmObj Scm_GetPortReaderLexicalMode(ScmPort *port)
{
/* We let it throw an error if there's no reader-lexical-mode attr.
It must be set in the constructor. */
return Scm_PortAttrGet(port, SCM_SYM_READER_LEXICAL_MODE, SCM_UNBOUND);
}
void Scm_SetPortReaderLexicalMode(ScmPort *port, ScmObj mode)
{
/*The check is duplicatd in Scm_SetReaderLexicalMode; refactoring needed.*/
if (!(SCM_EQ(mode, SCM_SYM_LEGACY)
|| SCM_EQ(mode, SCM_SYM_WARN_LEGACY)
|| SCM_EQ(mode, SCM_SYM_PERMISSIVE)
|| SCM_EQ(mode, SCM_SYM_STRICT_R7))) {
Scm_Error("reader-lexical-mode must be one of the following symbols:"
" legacy, warn-legacy, permissive, strict-r7, but got %S",
mode);
}
Scm_PortAttrSet(port, SCM_SYM_READER_LEXICAL_MODE, mode);
}
/* global reader lexical mode. */
ScmObj Scm_SetReaderLexicalMode(ScmObj mode)
{
if (!(SCM_EQ(mode, SCM_SYM_LEGACY)
|| SCM_EQ(mode, SCM_SYM_WARN_LEGACY)
|| SCM_EQ(mode, SCM_SYM_PERMISSIVE)
|| SCM_EQ(mode, SCM_SYM_STRICT_R7))) {
Scm_Error("reader-lexical-mode must be one of the following symbols:"
" legacy, warn-legacy, permissive, strict-r7, but got %S",
mode);
}
ScmObj prev_mode = Scm_ParameterRef(Scm_VM(), &readerLexicalMode);
Scm_ParameterSet(Scm_VM(), &readerLexicalMode, mode);
return prev_mode;
}
ScmObj Scm_ReaderLexicalMode()
{
return Scm_ParameterRef(Scm_VM(), &readerLexicalMode);
}
/* flushes the buffer, to make a room of cnt bytes.
cnt == 0 means all the available data. Note that, unless forcep == TRUE,
this function only does "best effort" to make room, but doesn't
guarantee to output cnt bytes. */
static void bufport_flush(ScmPort *p, int cnt, int forcep)
{
int cursiz = SCM_PORT_BUFFER_AVAIL(p);
if (cursiz == 0) return;
if (cnt <= 0) { cnt = cursiz; }
int nwrote = p->src.buf.flusher(p, cnt, forcep);
if (nwrote < 0) {
p->src.buf.current = p->src.buf.buffer; /* for safety */
p->error = TRUE;
/* TODO: can we raise an error here, or should we propagate
it to the caller? */
Scm_PortError(p, SCM_PORT_ERROR_OUTPUT,
"Couldn't flush port %S due to an error", p);
}
if (nwrote >= 0 && nwrote < cursiz) {
memmove(p->src.buf.buffer, p->src.buf.buffer+nwrote,
cursiz-nwrote);
p->src.buf.current -= nwrote;
} else {
p->src.buf.current = p->src.buf.buffer;
}
}
/* Writes siz bytes in src to the buffered port. siz may be larger than
the port's buffer. Won't return until entire siz bytes are written. */
static void bufport_write(ScmPort *p, const char *src, int siz)
{
do {
int room = (int)(p->src.buf.end - p->src.buf.current);
if (room >= siz) {
memcpy(p->src.buf.current, src, siz);
p->src.buf.current += siz;
siz = 0;
} else {
memcpy(p->src.buf.current, src, room);
p->src.buf.current += room;
siz -= room;
src += room;
bufport_flush(p, 0, FALSE);
}
} while (siz > 0);
}
/* Fills the buffer. Reads at least MIN bytes (unless it reaches EOF).
* If ALLOW_LESS is true, however, we allow to return before the full
* data is read.
* Returns the number of bytes actually read, or 0 if EOF, or -1 if error.
*/
static int bufport_fill(ScmPort *p, int min, int allow_less)
{
int cursiz = (int)(p->src.buf.end - p->src.buf.current);
int nread = 0, toread;
if (cursiz > 0) {
memmove(p->src.buf.buffer, p->src.buf.current, cursiz);
p->src.buf.current = p->src.buf.buffer;
p->src.buf.end = p->src.buf.current + cursiz;
} else {
p->src.buf.current = p->src.buf.end = p->src.buf.buffer;
}
if (min <= 0) min = SCM_PORT_BUFFER_ROOM(p);
if (SCM_PORT_BUFFER_MODE(p) != SCM_PORT_BUFFER_NONE) {
toread = SCM_PORT_BUFFER_ROOM(p);
} else {
toread = min;
}
do {
int r = p->src.buf.filler(p, toread-nread);
if (r <= 0) break;
nread += r;
p->src.buf.end += r;
} while (!allow_less && nread < min);
return nread;
}
/* Reads siz bytes to dst from the buffered port. siz may be larger
* than the port's buffer, in which case the filler procedure is called
* more than once. Unless the port buffering mode is BUFFER_FULL,
* this may read less than SIZ bytes if only that amount of data is
* immediately available.
* Caveat: if the filler procedure returns N where 0 < N < requested size,
* we know less data is available; non-greedy read can return at that point.
* However, if the filler procedure returns exactly the requested size,
* and we need more bytes, we gotta be careful -- next call to the filler
* procedure may or may not block. So we need to check the ready procedure.
*/
static int bufport_read(ScmPort *p, char *dst, int siz)
{
int nread = 0;
int avail = (int)(p->src.buf.end - p->src.buf.current);
int req = MIN(siz, avail);
if (req > 0) {
memcpy(dst, p->src.buf.current, req);
p->src.buf.current += req;
nread += req;
siz -= req;
dst += req;
}
while (siz > 0) {
/* We check data availability first, since we might already get
some data from the remanings in the buffer, and it is enough
if buffering mode is not full. */
if (nread && (SCM_PORT_BUFFER_MODE(p) != SCM_PORT_BUFFER_FULL)) {
if (p->src.buf.ready
&& p->src.buf.ready(p) == SCM_FD_WOULDBLOCK) {
break;
}
}
int req = MIN(siz, p->src.buf.size);
int r = bufport_fill(p, req, TRUE);
if (r <= 0) break; /* EOF or an error*/
if (r >= siz) {
memcpy(dst, p->src.buf.current, siz);
p->src.buf.current += siz;
nread += siz;
break;
} else {
memcpy(dst, p->src.buf.current, r);
p->src.buf.current += r;
nread += r;
siz -= r;
dst += r;
}
}
return nread;
}
/* Tracking buffered ports:
*
* The OS doesn't automatically flush the buffered output port,
* as it does on FILE* structure. So Gauche keeps track of active
* output buffered ports, in a weak vector.
* When the port is no longer used, it is collected by GC and removed
* from the vector. Scm_FlushAllPorts() flushes the active ports.
*
* Note that we don't remove entry from the weak vector explicitly.
* We used to do that in the port finalizer; however, the finalizer
* is called _after_ GC has run and determined the port is a garbage,
* and at that moment GC has already cleared the vector entry. So we
* can rather let GC remove the entries.
*
* When we find the weak vector is full, we trigger a global GC once.
* It may collect garbaged ports and make some room in the vector,
* even though the ports are not finalized (GC_gcollect doesn't call
* finalizers; they are called at the next checkpoint in VM).
*/
/*TODO: allow to extend the port vector. */
#define PORT_VECTOR_SIZE 256 /* need to be 2^n */
static struct {
int dummy;
ScmWeakVector *ports;
ScmInternalMutex mutex;
} active_buffered_ports = { 1, NULL }; /* magic to put this in .data area */
#define PORT_HASH(port) \
((((SCM_WORD(port)>>3) * 2654435761UL)>>16) % PORT_VECTOR_SIZE)
static void register_buffered_port(ScmPort *port)
{
int i, h, c;
int tried_gc = FALSE;
int need_gc = FALSE;
retry:
h = i = (int)PORT_HASH(port);
c = 0;
/* search an available entry by quadratic hash
used entry may have #<port> or #t. #t is for transient state
during Scm_FlushAllPorts()---see below. */
(void)SCM_INTERNAL_MUTEX_LOCK(active_buffered_ports.mutex);
while (!SCM_FALSEP(Scm_WeakVectorRef(active_buffered_ports.ports,
i, SCM_FALSE))) {
i -= ++c; while (i<0) i+=PORT_VECTOR_SIZE;
if (i == h) {
/* Vector entry is full. We run global GC to try to collect
unused entry. */
need_gc = TRUE;
break;
}
}
if (!need_gc) {
Scm_WeakVectorSet(active_buffered_ports.ports, i, SCM_OBJ(port));
}
(void)SCM_INTERNAL_MUTEX_UNLOCK(active_buffered_ports.mutex);
if (need_gc) {
if (tried_gc) {
/* We should probably try to extend the weak vector.
But for the time being... */
Scm_Panic("active buffered port table overflow");
} else {
GC_gcollect();
tried_gc = TRUE;
need_gc = FALSE;
goto retry;
}
}
}
/* This should be called when the output buffered port is explicitly closed.
The ports collected by GC are automatically unregistered. */
static void unregister_buffered_port(ScmPort *port)
{
int h = (int)PORT_HASH(port);
int i = h;
int c = 0;
(void)SCM_INTERNAL_MUTEX_LOCK(active_buffered_ports.mutex);
do {
ScmObj p = Scm_WeakVectorRef(active_buffered_ports.ports, i, SCM_FALSE);
if (!SCM_FALSEP(p) && SCM_EQ(SCM_OBJ(port), p)) {
Scm_WeakVectorSet(active_buffered_ports.ports, i, SCM_FALSE);
break;
}
i -= ++c; while (i<0) i+=PORT_VECTOR_SIZE;
} while (i != h);
(void)SCM_INTERNAL_MUTEX_UNLOCK(active_buffered_ports.mutex);
}
/* Flush all ports. Note that it is possible that this routine can be
called recursively if one of the flushing routine calls Scm_Exit.
In order to avoid infinite loop, I have to delete the entries of already
flushed port before calling flush, then recover them before return
(unless exitting is true, in that case we know nobody cares the active
port vector anymore).
Even if more than one thread calls Scm_FlushAllPorts simultaneously,
the flush method is called only once for each vector.
*/
void Scm_FlushAllPorts(int exitting)
{
ScmObj p = SCM_FALSE;
int saved = 0;
ScmVector *save = SCM_VECTOR(Scm_MakeVector(PORT_VECTOR_SIZE, SCM_FALSE));
ScmWeakVector *ports = active_buffered_ports.ports;
for (int i=0; i<PORT_VECTOR_SIZE;) {
(void)SCM_INTERNAL_MUTEX_LOCK(active_buffered_ports.mutex);
for (; i<PORT_VECTOR_SIZE; i++) {
p = Scm_WeakVectorRef(ports, i, SCM_FALSE);
if (SCM_PORTP(p)) {
Scm_VectorSet(save, i, p);
/* Set #t so that the slot won't be reused. */
Scm_WeakVectorSet(ports, i, SCM_TRUE);
saved++;
break;
}
}
(void)SCM_INTERNAL_MUTEX_UNLOCK(active_buffered_ports.mutex);
if (SCM_PORTP(p)) {
SCM_ASSERT(SCM_PORT_TYPE(p)==SCM_PORT_FILE);
if (!SCM_PORT_ERROR_OCCURRED_P(SCM_PORT(p))) {
bufport_flush(SCM_PORT(p), 0, TRUE);
}
}
}
if (!exitting && saved) {
(void)SCM_INTERNAL_MUTEX_LOCK(active_buffered_ports.mutex);
for (int i=0; i<PORT_VECTOR_SIZE; i++) {
p = Scm_VectorRef(save, i, SCM_FALSE);
if (SCM_PORTP(p)) Scm_WeakVectorSet(ports, i, p);
}
(void)SCM_INTERNAL_MUTEX_UNLOCK(active_buffered_ports.mutex);
}
}
/* Utility procedure to translate Scheme arg into buffering mode */
static ScmObj key_full = SCM_UNBOUND;
static ScmObj key_modest = SCM_UNBOUND;
static ScmObj key_line = SCM_UNBOUND;
static ScmObj key_none = SCM_UNBOUND;
int Scm_KeywordToBufferingMode(ScmObj flag, int direction, int fallback)
{
if (SCM_EQ(flag, key_full)) return SCM_PORT_BUFFER_FULL;
if (SCM_EQ(flag, key_none)) return SCM_PORT_BUFFER_NONE;
if (fallback >= 0 && (SCM_UNBOUNDP(flag) || SCM_FALSEP(flag)))
return fallback;
if (direction == SCM_PORT_INPUT) {
if (SCM_EQ(flag, key_modest)) return SCM_PORT_BUFFER_LINE;
else Scm_Error("buffering mode must be one of :full, :modest or :none, but got %S", flag);
}
if (direction == SCM_PORT_OUTPUT) {
if (SCM_EQ(flag, key_line)) return SCM_PORT_BUFFER_LINE;
else Scm_Error("buffering mode must be one of :full, :line or :none, but got %S", flag);
}
/* if direction is none of input or output, allow both. */