1 /* This file historically implemented the most platform-specific
2 aspects of Racket port types, which meant that it deals with all
3 the messy file descriptor issues, although the most
4 platform-specific part has moved to rktio. Also, `subprocess` is
5 implemented here. */
6
7 #include "schpriv.h"
8 #include "schmach.h"
9 #include "schrktio.h"
10 #include <errno.h>
11 #ifdef OS_X
12 /* needed for old gcc to define `off_t` */
13 # include <unistd.h>
14 #endif
15 #ifndef DONT_IGNORE_PIPE_SIGNAL
16 # include <signal.h>
17 #endif
18 #ifdef USE_ITIMER
19 # include <sys/time.h>
20 #endif
21
22 #define mzAssert(x) /* if (!(x)) abort() */
23
24 /******************** Generic FILEs ********************/
25
26 typedef struct {
27 MZTAG_IF_REQUIRED
28 FILE *f;
29 } Scheme_Input_File;
30
31 typedef struct {
32 MZTAG_IF_REQUIRED
33 FILE *f;
34 } Scheme_Output_File;
35
36 /******************** Subprocesses ********************/
37
38 typedef struct Scheme_Subprocess {
39 Scheme_Object so;
40 rktio_process_t *proc;
41 Scheme_Custodian_Reference *mref;
42 int is_group_rep;
43 } Scheme_Subprocess;
44
45 /******************** refcounts ********************/
46
47 #if defined(WINDOWS_FILE_HANDLES) || defined(MZ_USE_PLACES)
48 # define MZ_LOCK_REFCOUNTS
49 static mzrt_mutex *refcount_mutex;
50 #endif
51
malloc_refcount(int val,int free_on_zero)52 static int *malloc_refcount(int val, int free_on_zero)
53 {
54 int *rc;
55
56 #ifdef MZ_LOCK_REFCOUNTS
57 if (!refcount_mutex)
58 mzrt_mutex_create(&refcount_mutex);
59 #endif
60
61 rc = (int *)malloc(2 * sizeof(int));
62 *rc = val;
63 rc[1] = free_on_zero;
64
65 return rc;
66 }
67
adj_refcount(int * refcount,int amt)68 static int adj_refcount(int *refcount, int amt)
69 XFORM_SKIP_PROC
70 {
71 int rc;
72
73 if (!refcount)
74 return 0;
75
76 #ifdef MZ_LOCK_REFCOUNTS
77 mzrt_mutex_lock(refcount_mutex);
78 #endif
79 if (amt > 0) {
80 /* don't increment up from 0 */
81 if (*refcount)
82 *refcount += amt;
83 } else
84 *refcount += amt;
85 rc = *refcount;
86 #ifdef MZ_LOCK_REFCOUNTS
87 mzrt_mutex_unlock(refcount_mutex);
88 #endif
89
90 if (!rc && refcount[1])
91 free(refcount);
92
93 return rc;
94 }
95
96 /******************** file-descriptor I/O ********************/
97
98 static int *stdin_refcount, *stdout_refcount, *stderr_refcount;
99
100 # define MZPORT_FD_BUFFSIZE 4096
101 # define MZPORT_FD_DIRECT_THRESHOLD MZPORT_FD_BUFFSIZE
102
103 /* The Scheme_FD type is used for both input and output */
104 typedef struct Scheme_FD {
105 MZTAG_IF_REQUIRED
106 rktio_fd_t *fd;
107 intptr_t bufcount, buffpos;
108 char flushing, flush;
109 char *buffer;
110 int *refcount;
111 Scheme_Object *flush_handle; /* output port: registration with plumber */
112 /* For text mode and `port-file-position`: */
113 char *bufwidths;
114 } Scheme_FD;
115
scheme_port_name(Scheme_Object * p)116 Scheme_Object *scheme_port_name(Scheme_Object *p) {
117 if (p->type == scheme_input_port_type)
118 return ((Scheme_Input_Port *)p)->name;
119 else
120 return ((Scheme_Output_Port *)p)->name;
121 }
122
scheme_get_serialized_fd_flags(Scheme_Object * p,Scheme_Serialized_File_FD * so)123 int scheme_get_serialized_fd_flags(Scheme_Object* p, Scheme_Serialized_File_FD *so) {
124 Scheme_FD *fds;
125 if (p->type == scheme_input_port_type) {
126 fds = (Scheme_FD *) ((Scheme_Input_Port *)p)->port_data;
127 so->name = ((Scheme_Input_Port *)p)->name;
128 }
129 else {
130 fds = (Scheme_FD *) ((Scheme_Output_Port *)p)->port_data;
131 so->name = ((Scheme_Output_Port *)p)->name;
132 }
133 so->flush_mode = fds->flush;
134 return 1;
135 }
136
137 #define MZ_FLUSH_NEVER 0
138 #define MZ_FLUSH_BY_LINE 1
139 #define MZ_FLUSH_ALWAYS 2
140
141 #if defined(DOS_FILE_SYSTEM)
142 # if defined(__MINGW32__)
143 # define fseeko fseek
144 # define ftello ftell
145 # else
146 # define fseeko _fseeki64
147 # define ftello _ftelli64
148 # endif
149 #endif
150
151 /******************** Globals and Prototypes ********************/
152
153 /* globals */
154
155 READ_ONLY Scheme_Object scheme_eof[1];
156 THREAD_LOCAL_DECL(Scheme_Object *scheme_orig_stdout_port);
157 THREAD_LOCAL_DECL(Scheme_Object *scheme_orig_stderr_port);
158 THREAD_LOCAL_DECL(Scheme_Object *scheme_orig_stdin_port);
159
160 HOOK_SHARED_OK Scheme_Object *(*scheme_make_stdin)(void) = NULL;
161 HOOK_SHARED_OK Scheme_Object *(*scheme_make_stdout)(void) = NULL;
162 HOOK_SHARED_OK Scheme_Object *(*scheme_make_stderr)(void) = NULL;
163
164 SHARED_OK MZ_DLLSPEC int scheme_binary_mode_stdio = 0;
scheme_set_binary_mode_stdio(int v)165 void scheme_set_binary_mode_stdio(int v) { scheme_binary_mode_stdio = v; }
166
167 THREAD_LOCAL_DECL(static int special_is_ok);
168
169 /* locals */
170
171 THREAD_LOCAL_DECL(static int fd_reserved);
172 THREAD_LOCAL_DECL(static rktio_fd_t *the_fd);
173
174 READ_ONLY static Scheme_Object *fd_input_port_type;
175 READ_ONLY static Scheme_Object *file_input_port_type;
176 READ_ONLY Scheme_Object *scheme_string_input_port_type;
177 READ_ONLY Scheme_Object *scheme_tcp_input_port_type;
178 READ_ONLY Scheme_Object *scheme_tcp_output_port_type;
179 READ_ONLY static Scheme_Object *fd_output_port_type;
180 READ_ONLY static Scheme_Object *file_output_port_type;
181 READ_ONLY Scheme_Object *scheme_string_output_port_type;
182 READ_ONLY Scheme_Object *scheme_user_input_port_type;
183 READ_ONLY Scheme_Object *scheme_user_output_port_type;
184 READ_ONLY Scheme_Object *scheme_pipe_read_port_type;
185 READ_ONLY Scheme_Object *scheme_pipe_write_port_type;
186 READ_ONLY Scheme_Object *scheme_null_output_port_type;
187 READ_ONLY Scheme_Object *scheme_redirect_output_port_type;
188
189 THREAD_LOCAL_DECL(int scheme_force_port_closed);
190
191 SHARED_OK static int flush_out;
192 SHARED_OK static int flush_err;
193
194 THREAD_LOCAL_DECL(static Scheme_Custodian *new_port_cust); /* back-door argument */
195
196 static void register_port_wait();
197
198 static intptr_t flush_fd(Scheme_Output_Port *op,
199 const char * volatile bufstr, volatile uintptr_t buflen,
200 volatile uintptr_t offset, int immediate_only, int enable_break);
201 static void flush_if_output_fds(Scheme_Object *o, Scheme_Close_Custodian_Client *f, void *data);
202
203 static Scheme_Object *subprocess(int c, Scheme_Object *args[]);
204 static Scheme_Object *subprocess_status(int c, Scheme_Object *args[]);
205 static Scheme_Object *subprocess_kill(int c, Scheme_Object *args[]);
206 static Scheme_Object *subprocess_pid(int c, Scheme_Object *args[]);
207 static Scheme_Object *subprocess_p(int c, Scheme_Object *args[]);
208 static Scheme_Object *subprocess_wait(int c, Scheme_Object *args[]);
209 static Scheme_Object *sch_shell_execute(int c, Scheme_Object *args[]);
210 static Scheme_Object *current_subproc_cust_mode (int, Scheme_Object *[]);
211 static Scheme_Object *subproc_group_on (int, Scheme_Object *[]);
212 static void register_subprocess_wait();
213
214 static void block_timer_signals(int block);
215
216 static Scheme_Object *unsafe_fd_to_port(int, Scheme_Object *[]);
217 static Scheme_Object *unsafe_port_to_fd(int, Scheme_Object *[]);
218 static Scheme_Object *unsafe_fd_to_semaphore(int, Scheme_Object *[]);
219 static Scheme_Object *unsafe_socket_to_port(int, Scheme_Object *[]);
220 static Scheme_Object *unsafe_port_to_socket(int, Scheme_Object *[]);
221 static Scheme_Object *unsafe_socket_to_semaphore(int, Scheme_Object *[]);
222
223 typedef struct Scheme_Read_Write_Evt {
224 Scheme_Object so;
225 Scheme_Object *port;
226 Scheme_Object *v; /* peek skip or writeable special */
227 char *str;
228 intptr_t start, size;
229 } Scheme_Read_Write_Evt;
230
231 static int rw_evt_ready(Scheme_Object *rww, Scheme_Schedule_Info *sinfo);
232 static void rw_evt_wakeup(Scheme_Object *rww, void *fds);
233
234 static int progress_evt_ready(Scheme_Object *rww, Scheme_Schedule_Info *sinfo);
235 static int closed_evt_ready(Scheme_Object *rww, Scheme_Schedule_Info *sinfo);
236 static int filesystem_change_evt_ready(Scheme_Object *evt, Scheme_Schedule_Info *sinfo);
237
238 static void filesystem_change_evt_need_wakeup (Scheme_Object *port, void *fds);
239
240 static Scheme_Object *
241 _scheme_make_named_file_input_port(FILE *fp, Scheme_Object *name, int regfile);
242 static void default_sleep(float v, void *fds);
243 #ifdef MZ_PRECISE_GC
244 static void register_traversers(void);
245 #endif
246
247 static Scheme_Object *make_fd_input_port(rktio_fd_t *fd, Scheme_Object *name, int *refcount, int internal);
248 static Scheme_Object *make_fd_output_port(rktio_fd_t *fd, Scheme_Object *name, int read_too, int flush_mode, int *refcount);
249
250 static void force_close_output_port(Scheme_Object *port);
251 static void force_close_input_port(Scheme_Object *port);
252
253 ROSYM static Scheme_Object *text_symbol, *binary_symbol, *module_symbol;
254 ROSYM static Scheme_Object *append_symbol, *error_symbol, *update_symbol, *can_update_symbol;
255 ROSYM static Scheme_Object *replace_symbol, *truncate_symbol, *truncate_replace_symbol;
256 ROSYM static Scheme_Object *must_truncate_symbol;
257
258 ROSYM Scheme_Object *scheme_none_symbol, *scheme_line_symbol, *scheme_block_symbol;
259
260 ROSYM static Scheme_Object *exact_symbol, *new_symbol;
261
262 #define READ_STRING_BYTE_BUFFER_SIZE 1024
263 THREAD_LOCAL_DECL(static char *read_string_byte_buffer);
264
265 #define fail_err_symbol scheme_false
266
267 typedef struct Scheme_Filesystem_Change_Evt {
268 Scheme_Object so;
269 rktio_fs_change_t *rfc;
270 Scheme_Custodian_Reference *mref;
271 } Scheme_Filesystem_Change_Evt;
272
273 /*========================================================================*/
274 /* initialization */
275 /*========================================================================*/
276
277 void
scheme_init_port(Scheme_Startup_Env * env)278 scheme_init_port (Scheme_Startup_Env *env)
279 {
280 #ifdef MZ_PRECISE_GC
281 register_traversers();
282 #endif
283
284 REGISTER_SO(text_symbol);
285 REGISTER_SO(binary_symbol);
286 REGISTER_SO(module_symbol);
287 REGISTER_SO(append_symbol);
288 REGISTER_SO(error_symbol);
289 REGISTER_SO(replace_symbol);
290 REGISTER_SO(truncate_symbol);
291 REGISTER_SO(truncate_replace_symbol);
292 REGISTER_SO(update_symbol);
293 REGISTER_SO(can_update_symbol);
294 REGISTER_SO(must_truncate_symbol);
295
296 text_symbol = scheme_intern_symbol("text");
297 binary_symbol = scheme_intern_symbol("binary");
298 module_symbol = scheme_intern_symbol("module");
299 append_symbol = scheme_intern_symbol("append");
300 error_symbol = scheme_intern_symbol("error");
301 replace_symbol = scheme_intern_symbol("replace");
302 truncate_symbol = scheme_intern_symbol("truncate");
303 truncate_replace_symbol = scheme_intern_symbol("truncate/replace");
304 update_symbol = scheme_intern_symbol("update");
305 can_update_symbol = scheme_intern_symbol("can-update");
306 must_truncate_symbol = scheme_intern_symbol("must-truncate");
307
308 REGISTER_SO(scheme_none_symbol);
309 REGISTER_SO(scheme_line_symbol);
310 REGISTER_SO(scheme_block_symbol);
311
312 scheme_none_symbol = scheme_intern_symbol("none");
313 scheme_line_symbol = scheme_intern_symbol("line");
314 scheme_block_symbol = scheme_intern_symbol("block");
315
316 REGISTER_SO(exact_symbol);
317 REGISTER_SO(new_symbol);
318
319 exact_symbol = scheme_intern_symbol("exact");
320 new_symbol = scheme_intern_symbol("new");
321
322 REGISTER_SO(fd_input_port_type);
323 REGISTER_SO(fd_output_port_type);
324 REGISTER_SO(file_input_port_type);
325 REGISTER_SO(scheme_string_input_port_type);
326 REGISTER_SO(scheme_tcp_input_port_type);
327 REGISTER_SO(scheme_tcp_output_port_type);
328 REGISTER_SO(file_output_port_type);
329 REGISTER_SO(scheme_string_output_port_type);
330 REGISTER_SO(scheme_user_input_port_type);
331 REGISTER_SO(scheme_user_output_port_type);
332 REGISTER_SO(scheme_pipe_read_port_type);
333 REGISTER_SO(scheme_pipe_write_port_type);
334 REGISTER_SO(scheme_null_output_port_type);
335 REGISTER_SO(scheme_redirect_output_port_type);
336
337 #ifndef DONT_IGNORE_PIPE_SIGNAL
338 scheme_set_signal_handler(SIGPIPE, NULL);
339 #endif
340
341 if (!scheme_sleep)
342 scheme_sleep = default_sleep;
343
344 scheme_eof->type = scheme_eof_type;
345
346 scheme_string_input_port_type = scheme_make_port_type("<string-input-port>");
347 scheme_string_output_port_type = scheme_make_port_type("<string-output-port>");
348
349 fd_input_port_type = scheme_make_port_type("<stream-input-port>");
350 fd_output_port_type = scheme_make_port_type("<stream-output-port>");
351
352 file_input_port_type = scheme_make_port_type("<file-input-port>");
353 file_output_port_type = scheme_make_port_type("<file-output-port>");
354
355 scheme_user_input_port_type = scheme_make_port_type("<user-input-port>");
356 scheme_user_output_port_type = scheme_make_port_type("<user-output-port>");
357
358 scheme_pipe_read_port_type = scheme_make_port_type("<pipe-input-port>");
359 scheme_pipe_write_port_type = scheme_make_port_type("<pipe-output-port>");
360
361 scheme_tcp_input_port_type = scheme_make_port_type("<tcp-input-port>");
362 scheme_tcp_output_port_type = scheme_make_port_type("<tcp-output-port>");
363
364 scheme_null_output_port_type = scheme_make_port_type("<null-output-port>");
365 scheme_redirect_output_port_type = scheme_make_port_type("<redirect-output-port>");
366
367 scheme_addto_prim_instance("subprocess", scheme_make_prim_w_arity2(subprocess, "subprocess", 4, -1, 4, 4), env);
368 scheme_addto_prim_instance("subprocess-status", scheme_make_prim_w_arity(subprocess_status, "subprocess-status", 1, 1), env);
369 scheme_addto_prim_instance("subprocess-kill", scheme_make_prim_w_arity(subprocess_kill, "subprocess-kill", 2, 2), env);
370 scheme_addto_prim_instance("subprocess-pid", scheme_make_prim_w_arity(subprocess_pid, "subprocess-pid", 1, 1), env);
371 scheme_addto_prim_instance("subprocess?", scheme_make_prim_w_arity(subprocess_p, "subprocess?", 1, 1), env);
372 scheme_addto_prim_instance("subprocess-wait", scheme_make_prim_w_arity(subprocess_wait, "subprocess-wait", 1, 1), env);
373
374 ADD_PARAMETER("subprocess-group-enabled", subproc_group_on, MZCONFIG_SUBPROC_GROUP_ENABLED, env);
375 ADD_PARAMETER("current-subprocess-custodian-mode", current_subproc_cust_mode, MZCONFIG_SUBPROC_CUSTODIAN_MODE, env);
376
377 scheme_addto_prim_instance("shell-execute", scheme_make_prim_w_arity(sch_shell_execute, "shell-execute", 5, 5), env);
378 }
379
scheme_init_port_wait()380 void scheme_init_port_wait()
381 {
382 register_port_wait();
383 register_subprocess_wait();
384
385 scheme_add_evt(scheme_progress_evt_type, (Scheme_Ready_Fun)progress_evt_ready, NULL, NULL, 1);
386 scheme_add_evt(scheme_write_evt_type, (Scheme_Ready_Fun)rw_evt_ready, rw_evt_wakeup, NULL, 1);
387 scheme_add_evt(scheme_port_closed_evt_type, (Scheme_Ready_Fun)closed_evt_ready, NULL, NULL, 1);
388 scheme_add_evt(scheme_filesystem_change_evt_type, (Scheme_Ready_Fun)filesystem_change_evt_ready,
389 filesystem_change_evt_need_wakeup, NULL, 1);
390 }
391
scheme_init_unsafe_port(Scheme_Startup_Env * env)392 void scheme_init_unsafe_port (Scheme_Startup_Env *env)
393 {
394 ADD_PRIM_W_ARITY("unsafe-file-descriptor->port", unsafe_fd_to_port, 3, 3, env);
395 ADD_PRIM_W_ARITY("unsafe-port->file-descriptor", unsafe_port_to_fd, 1, 1, env);
396 ADD_PRIM_W_ARITY("unsafe-file-descriptor->semaphore", unsafe_fd_to_semaphore, 2, 2, env);
397
398 ADD_PRIM_W_ARITY("unsafe-socket->port", unsafe_socket_to_port, 3, 3, env);
399 ADD_PRIM_W_ARITY("unsafe-port->socket", unsafe_port_to_socket, 1, 1, env);
400 ADD_PRIM_W_ARITY("unsafe-socket->semaphore", unsafe_socket_to_semaphore, 2, 2, env);
401 }
402
scheme_init_port_places(void)403 void scheme_init_port_places(void)
404 {
405
406 scheme_add_atexit_closer(flush_if_output_fds);
407 /* Note: other threads might continue to write even after
408 the flush completes, but that's the threads' problem.
409 All writing by the main thread will get flushed on exit
410 (but not, of course, if the thread is shutdown via a
411 custodian). */
412
413 if (!stdin_refcount) {
414 /* Reference counts are needed for stdio and places; start
415 at 1 in main place, but then cancel initial count */
416 stdin_refcount = malloc_refcount(1, 0);
417 stdout_refcount = malloc_refcount(1, 0);
418 stderr_refcount = malloc_refcount(1, 0);
419 }
420
421 REGISTER_SO(read_string_byte_buffer);
422 REGISTER_SO(scheme_orig_stdout_port);
423 REGISTER_SO(scheme_orig_stderr_port);
424 REGISTER_SO(scheme_orig_stdin_port);
425 scheme_orig_stdin_port = (scheme_make_stdin
426 ? scheme_make_stdin()
427 : make_fd_input_port(rktio_std_fd(scheme_rktio, RKTIO_STDIN), scheme_intern_symbol("stdin"),
428 stdin_refcount, 0));
429
430 scheme_orig_stdout_port = (scheme_make_stdout
431 ? scheme_make_stdout()
432 : make_fd_output_port(rktio_std_fd(scheme_rktio, RKTIO_STDOUT), scheme_intern_symbol("stdout"), 0,
433 -1, stdout_refcount));
434
435 scheme_orig_stderr_port = (scheme_make_stderr
436 ? scheme_make_stderr()
437 : make_fd_output_port(rktio_std_fd(scheme_rktio, RKTIO_STDERR), scheme_intern_symbol("stderr"), 0,
438 MZ_FLUSH_ALWAYS, stderr_refcount));
439
440 if (!scheme_current_place_id) {
441 adj_refcount(stdin_refcount, -1);
442 adj_refcount(stdout_refcount, -1);
443 adj_refcount(stderr_refcount, -1);
444 }
445
446 flush_out = SCHEME_TRUEP(scheme_terminal_port_p(1, &scheme_orig_stdout_port));
447 flush_err = SCHEME_TRUEP(scheme_terminal_port_p(1, &scheme_orig_stderr_port));
448 }
449
scheme_init_port_config(void)450 void scheme_init_port_config(void)
451 {
452 Scheme_Config *config;
453
454 config = scheme_current_config();
455
456 scheme_set_param(config, MZCONFIG_INPUT_PORT, scheme_orig_stdin_port);
457 scheme_set_param(config, MZCONFIG_OUTPUT_PORT, scheme_orig_stdout_port);
458 scheme_set_param(config, MZCONFIG_ERROR_PORT, scheme_orig_stderr_port);
459 }
460
scheme_make_eof(void)461 Scheme_Object * scheme_make_eof (void)
462 {
463 return scheme_eof;
464 }
465
scheme_set_stdio_makers(Scheme_Stdio_Maker_Proc in,Scheme_Stdio_Maker_Proc out,Scheme_Stdio_Maker_Proc err)466 void scheme_set_stdio_makers(Scheme_Stdio_Maker_Proc in,
467 Scheme_Stdio_Maker_Proc out,
468 Scheme_Stdio_Maker_Proc err)
469 {
470 scheme_make_stdin = in;
471 scheme_make_stdout = out;
472 scheme_make_stderr = err;
473 }
474
475 /*========================================================================*/
476 /* fd arrays */
477 /*========================================================================*/
478
479 /* This compatibility layer assumes that poll sets are allocated at
480 8-byte boundaries, so bites 2 and 3 can be used to indicate the
481 write and exception offsets. */
482
483 #define EXTRACT_FD_OFFSET(p) ((((uintptr_t)p)&0x6) >> 1)
484 #define EXTRACT_FD_BASE(p) ((void *)(((uintptr_t)p)&~(uintptr_t)0x6))
485
scheme_alloc_fdset_array(int count,int permanent)486 void *scheme_alloc_fdset_array(int count, int permanent)
487 {
488 return rktio_make_poll_set(scheme_rktio);
489 }
490
scheme_get_fdset(void * fdarray,int pos)491 void *scheme_get_fdset(void *fdarray, int pos)
492 {
493 return (void *)(((uintptr_t)fdarray) | (2 * pos));
494 }
495
scheme_fdzero(void * fds)496 void scheme_fdzero(void *fds)
497 {
498 scheme_signal_error("scheme_fdzero is not supported");
499 }
500
scheme_fdset(void * fds,int pos)501 void scheme_fdset(void *fds, int pos)
502 {
503 int offset = EXTRACT_FD_OFFSET(fds);
504 fds = EXTRACT_FD_BASE(fds);
505
506 if (offset != 2) {
507 rktio_fd_t *rfd;
508 rfd = rktio_system_fd(scheme_rktio, pos, RKTIO_OPEN_SOCKET);
509 rktio_poll_add(scheme_rktio, rfd, fds, (offset ? RKTIO_OPEN_WRITE : RKTIO_OPEN_READ));
510 rktio_forget(scheme_rktio, rfd);
511 }
512 }
513
scheme_fdclr(void * fds,int pos)514 void scheme_fdclr(void *fds, int pos)
515 {
516 scheme_signal_error("scheme_fdclr is not supported");
517 }
518
scheme_fdisset(void * fds,int pos)519 int scheme_fdisset(void *fds, int pos)
520 {
521 scheme_signal_error("scheme_fdisset is not supported");
522 return 0;
523 }
524
scheme_collapse_win_fd(void * fds)525 void scheme_collapse_win_fd(void *fds)
526 {
527 scheme_signal_error("scheme_collapse_win_fd is not supported");
528 }
529
scheme_add_fd_handle(void * h,void * fds,int repost)530 void scheme_add_fd_handle(void *h, void *fds, int repost)
531 {
532 fds = EXTRACT_FD_BASE(fds);
533 rktio_poll_set_add_handle(scheme_rktio, (intptr_t)h, fds, repost);
534 }
535
scheme_add_fd_nosleep(void * fds)536 void scheme_add_fd_nosleep(void *fds)
537 {
538 fds = EXTRACT_FD_BASE(fds);
539 rktio_poll_set_add_nosleep(scheme_rktio, fds);
540 }
541
scheme_add_fd_eventmask(void * fds,int mask)542 void scheme_add_fd_eventmask(void *fds, int mask)
543 {
544 fds = EXTRACT_FD_BASE(fds);
545 rktio_poll_set_add_eventmask(scheme_rktio, fds, mask);
546 }
547
548 /*========================================================================*/
549 /* Windows thread suspension */
550 /*========================================================================*/
551
552 /* Racket creates Windows threads for various purposes, including
553 non-blocking FILE reads. Unfortunately, these threads can confuse
554 the Boehm GC if they move virtual pages around while its marking. So we
555 remember each created thread and suspend it during GC.
556
557 TODO: We don't have Boehm GC anymore - it might be a good time to understand
558 if this is still required. */
559
560 #ifndef MZ_PRECISE_GC
561
562 # ifdef WINDOWS_PROCESSES
563 typedef struct Scheme_Thread_Memory {
564 MZTAG_IF_REQUIRED
565 void *handle;
566 void *subhandle;
567 int autoclose;
568 struct Scheme_Thread_Memory *prev;
569 struct Scheme_Thread_Memory *next;
570 } Scheme_Thread_Memory;
571
572 Scheme_Thread_Memory *tm_start, *tm_next;
573
scheme_init_thread_memory()574 void scheme_init_thread_memory()
575 {
576 REGISTER_SO(tm_start);
577 REGISTER_SO(tm_next);
578
579 /* We start with a pre-allocated tm because we
580 want to register a thread before performing any
581 allocations. */
582 tm_next = MALLOC_ONE_RT(Scheme_Thread_Memory);
583 # ifdef MZTAG_REQUIRED
584 tm_next->type = scheme_rt_thread_memory;
585 # endif
586
587 /* scheme_init_thread() will replace these: */
588 GC_set_collect_start_callback(scheme_suspend_remembered_threads);
589 GC_set_collect_end_callback(scheme_resume_remembered_threads);
590 }
591
scheme_remember_thread(void * t,int autoclose)592 Scheme_Thread_Memory *scheme_remember_thread(void *t, int autoclose)
593 {
594 Scheme_Thread_Memory *tm = tm_next;
595
596 tm->handle = t;
597 tm->subhandle = NULL;
598 tm->autoclose = autoclose;
599
600 tm->prev = NULL;
601 tm->next = tm_start;
602 if (tm->next)
603 tm->next->prev = tm;
604 tm_start = tm;
605
606 tm_next = MALLOC_ONE_RT(Scheme_Thread_Memory);
607 # ifdef MZTAG_REQUIRED
608 tm_next->type = scheme_rt_thread_memory;
609 # endif
610
611 return tm;
612 }
613
scheme_remember_subthread(struct Scheme_Thread_Memory * tm,void * t)614 void scheme_remember_subthread(struct Scheme_Thread_Memory *tm, void *t)
615 {
616 tm->subhandle = t;
617 }
618
scheme_forget_thread(struct Scheme_Thread_Memory * tm)619 void scheme_forget_thread(struct Scheme_Thread_Memory *tm)
620 XFORM_SKIP_PROC
621 {
622 if (tm->prev)
623 tm->prev->next = tm->next;
624 else
625 tm_start = tm->next;
626
627 if (tm->next)
628 tm->next->prev = tm->prev;
629
630 tm->next = NULL;
631 tm->prev = NULL;
632 }
633
scheme_forget_subthread(struct Scheme_Thread_Memory * tm)634 void scheme_forget_subthread(struct Scheme_Thread_Memory *tm)
635 XFORM_SKIP_PROC
636 {
637 tm->subhandle = NULL;
638 }
639
scheme_suspend_remembered_threads(void)640 void scheme_suspend_remembered_threads(void)
641 XFORM_SKIP_PROC
642 {
643 Scheme_Thread_Memory *tm, *next, *prev = NULL;
644 int keep;
645
646 for (tm = tm_start; tm; tm = next) {
647 next = tm->next;
648
649 keep = 1;
650 if (tm->autoclose) {
651 if (WaitForSingleObject(tm->handle, 0) == WAIT_OBJECT_0) {
652 CloseHandle((HANDLE)tm->handle);
653 tm->handle = NULL;
654 if (prev)
655 prev->next = tm->next;
656 else
657 tm_start = tm->next;
658 if (tm->next)
659 tm->next->prev = prev;
660 tm->next = NULL;
661 tm->prev = NULL;
662 keep = 0;
663 }
664 }
665
666 if (keep) {
667 SuspendThread((HANDLE)tm->handle);
668 if (tm->subhandle)
669 SuspendThread((HANDLE)tm->subhandle);
670 prev = tm;
671 }
672 }
673 }
674
scheme_resume_remembered_threads(void)675 void scheme_resume_remembered_threads(void)
676 XFORM_SKIP_PROC
677 {
678 Scheme_Thread_Memory *tm;
679
680 for (tm = tm_start; tm; tm = tm->next) {
681 if (tm->subhandle)
682 ResumeThread((HANDLE)tm->subhandle);
683 ResumeThread((HANDLE)tm->handle);
684 }
685 }
686
687 # endif
688
689 #else
690
691 typedef struct Scheme_Thread_Memory Scheme_Thread_Memory;
692
scheme_init_thread_memory()693 void scheme_init_thread_memory() { }
scheme_remember_thread(void * t,int autoclose)694 Scheme_Thread_Memory *scheme_remember_thread(void *t, int autoclose) { return NULL; }
scheme_remember_subthread(struct Scheme_Thread_Memory * tm,void * t)695 void scheme_remember_subthread(struct Scheme_Thread_Memory *tm, void *t) { }
scheme_forget_thread(struct Scheme_Thread_Memory * tm)696 void scheme_forget_thread(struct Scheme_Thread_Memory *tm) { }
scheme_forget_subthread(struct Scheme_Thread_Memory * tm)697 void scheme_forget_subthread(struct Scheme_Thread_Memory *tm) { }
scheme_suspend_remembered_threads()698 void scheme_suspend_remembered_threads() { }
scheme_resume_remembered_threads(void)699 void scheme_resume_remembered_threads(void) { }
700
701 #endif
702
703
704 /*========================================================================*/
705 /* Generic port support */
706 /*========================================================================*/
707
scheme_make_port_type(const char * name)708 Scheme_Object *scheme_make_port_type(const char *name)
709 {
710 return scheme_make_symbol(name);
711 }
712
init_port_locations(Scheme_Port * ip)713 static void init_port_locations(Scheme_Port *ip)
714 {
715 int cl;
716
717 ip->position = 0;
718 ip->readpos = 0; /* like position, but post UTF-8 decoding, collapses CRLF, etc. */
719 ip->lineNumber = 1;
720 ip->oldColumn = 0;
721 ip->column = 0;
722 ip->charsSinceNewline = 1;
723 cl = SCHEME_TRUEP(scheme_get_param(scheme_current_config(), MZCONFIG_PORT_COUNT_LINES));
724 ip->count_lines = cl;
725 }
726
scheme_set_next_port_custodian(Scheme_Custodian * c)727 void scheme_set_next_port_custodian(Scheme_Custodian *c)
728 {
729 new_port_cust = c;
730 }
731
732 Scheme_Input_Port *
scheme_make_input_port(Scheme_Object * subtype,void * data,Scheme_Object * name,Scheme_Get_String_Fun get_string_fun,Scheme_Peek_String_Fun peek_string_fun,Scheme_Progress_Evt_Fun progress_evt_fun,Scheme_Peeked_Read_Fun peeked_read_fun,Scheme_In_Ready_Fun byte_ready_fun,Scheme_Close_Input_Fun close_fun,Scheme_Need_Wakeup_Input_Fun need_wakeup_fun,int must_close)733 scheme_make_input_port(Scheme_Object *subtype,
734 void *data,
735 Scheme_Object *name,
736 Scheme_Get_String_Fun get_string_fun,
737 Scheme_Peek_String_Fun peek_string_fun,
738 Scheme_Progress_Evt_Fun progress_evt_fun,
739 Scheme_Peeked_Read_Fun peeked_read_fun,
740 Scheme_In_Ready_Fun byte_ready_fun,
741 Scheme_Close_Input_Fun close_fun,
742 Scheme_Need_Wakeup_Input_Fun need_wakeup_fun,
743 int must_close)
744 {
745 Scheme_Input_Port *ip;
746 Scheme_Custodian *cust = new_port_cust;
747
748 new_port_cust = NULL;
749
750 ip = MALLOC_ONE_TAGGED(Scheme_Input_Port);
751 ip->p.so.type = scheme_input_port_type;
752 ip->sub_type = subtype;
753 ip->port_data = data;
754 ip->get_string_fun = get_string_fun;
755 ip->peek_string_fun = peek_string_fun;
756 ip->progress_evt_fun = progress_evt_fun;
757 ip->peeked_read_fun = peeked_read_fun;
758 ip->byte_ready_fun = byte_ready_fun;
759 ip->need_wakeup_fun = need_wakeup_fun;
760 ip->close_fun = close_fun;
761 ip->name = name;
762 ip->ungotten_count = 0;
763 ip->closed = 0;
764 ip->read_handler = NULL;
765 init_port_locations((Scheme_Port *)ip);
766 if (ip->p.count_lines) ip->slow = 1;
767
768 if (progress_evt_fun == scheme_progress_evt_via_get)
769 ip->unless_cache = scheme_false;
770
771 if (must_close) {
772 Scheme_Custodian_Reference *mref;
773 mref = scheme_add_managed(cust,
774 (Scheme_Object *)ip,
775 (Scheme_Close_Custodian_Client *)force_close_input_port,
776 NULL, must_close);
777 ip->mref = mref;
778 } else
779 ip->mref = NULL;
780
781 return (ip);
782 }
783
scheme_set_port_location_fun(Scheme_Port * port,Scheme_Location_Fun location_fun)784 void scheme_set_port_location_fun(Scheme_Port *port,
785 Scheme_Location_Fun location_fun)
786 {
787 port->location_fun = location_fun;
788 }
789
scheme_set_port_count_lines_fun(Scheme_Port * port,Scheme_Count_Lines_Fun count_lines_fun)790 void scheme_set_port_count_lines_fun(Scheme_Port *port,
791 Scheme_Count_Lines_Fun count_lines_fun)
792 {
793 port->count_lines_fun = count_lines_fun;
794 }
795
evt_input_port_p(Scheme_Object * p)796 static int evt_input_port_p(Scheme_Object *p)
797 {
798 return 1;
799 }
800
801 Scheme_Output_Port *
scheme_make_output_port(Scheme_Object * subtype,void * data,Scheme_Object * name,Scheme_Write_String_Evt_Fun write_string_evt_fun,Scheme_Write_String_Fun write_string_fun,Scheme_Out_Ready_Fun ready_fun,Scheme_Close_Output_Fun close_fun,Scheme_Need_Wakeup_Output_Fun need_wakeup_fun,Scheme_Write_Special_Evt_Fun write_special_evt_fun,Scheme_Write_Special_Fun write_special_fun,int must_close)802 scheme_make_output_port(Scheme_Object *subtype,
803 void *data,
804 Scheme_Object *name,
805 Scheme_Write_String_Evt_Fun write_string_evt_fun,
806 Scheme_Write_String_Fun write_string_fun,
807 Scheme_Out_Ready_Fun ready_fun,
808 Scheme_Close_Output_Fun close_fun,
809 Scheme_Need_Wakeup_Output_Fun need_wakeup_fun,
810 Scheme_Write_Special_Evt_Fun write_special_evt_fun,
811 Scheme_Write_Special_Fun write_special_fun,
812 int must_close)
813 {
814 Scheme_Output_Port *op;
815 Scheme_Custodian *cust = new_port_cust;
816
817 new_port_cust = NULL;
818
819 op = MALLOC_ONE_TAGGED(Scheme_Output_Port);
820 op->p.so.type = scheme_output_port_type;
821 op->sub_type = subtype;
822 op->port_data = data;
823 op->name = name;
824 op->write_string_evt_fun = write_string_evt_fun;
825 op->write_string_fun = write_string_fun;
826 op->close_fun = close_fun;
827 op->ready_fun = ready_fun;
828 op->need_wakeup_fun = need_wakeup_fun;
829 op->write_special_evt_fun = write_special_evt_fun;
830 op->write_special_fun = write_special_fun;
831 op->closed = 0;
832 op->display_handler = NULL;
833 op->write_handler = NULL;
834 op->print_handler = NULL;
835 init_port_locations((Scheme_Port *)op);
836
837 if (must_close) {
838 Scheme_Custodian_Reference *mref;
839 mref = scheme_add_managed(cust,
840 (Scheme_Object *)op,
841 (Scheme_Close_Custodian_Client *)force_close_output_port,
842 NULL, must_close);
843 op->mref = mref;
844 } else
845 op->mref = NULL;
846
847 return op;
848 }
849
evt_output_port_p(Scheme_Object * p)850 static int evt_output_port_p(Scheme_Object *p)
851 {
852 return 1;
853 }
854
output_ready(Scheme_Object * port,Scheme_Schedule_Info * sinfo)855 static int output_ready(Scheme_Object *port, Scheme_Schedule_Info *sinfo)
856 {
857 Scheme_Output_Port *op;
858
859 op = scheme_output_port_record(port);
860
861 if (op->closed)
862 return 1;
863
864 if (SAME_OBJ(scheme_user_output_port_type, op->sub_type)) {
865 /* We can't call the normal ready because that might run Racket
866 code, and this function is called by the scheduler when
867 false_pos_ok is true. So, in that case, we assume that if the
868 port's evt is ready, then the port is ready. (After
869 all, false positives are ok in that mode.) Even when the
870 scheduler isn't requesting the status, we need sinfo. */
871 return scheme_user_port_write_probably_ready(op, sinfo);
872 }
873
874 if (op->ready_fun) {
875 Scheme_Out_Ready_Fun_FPC rf;
876 rf = (Scheme_Out_Ready_Fun_FPC)op->ready_fun;
877 return rf(op, sinfo);
878 }
879
880 return 1;
881 }
882
output_need_wakeup(Scheme_Object * port,void * fds)883 static void output_need_wakeup (Scheme_Object *port, void *fds)
884 {
885 Scheme_Output_Port *op;
886
887 /* If this is a user output port and its evt needs a wakeup, we
888 shouldn't get here. The target use above will take care of it. */
889
890 op = scheme_output_port_record(port);
891 if (op->need_wakeup_fun) {
892 Scheme_Need_Wakeup_Output_Fun f;
893 f = op->need_wakeup_fun;
894 f(op, fds);
895 }
896 }
897
898 static int byte_input_ready (Scheme_Object *port, Scheme_Schedule_Info *sinfo);
899
scheme_byte_ready_or_user_port_ready(Scheme_Object * p,Scheme_Schedule_Info * sinfo)900 int scheme_byte_ready_or_user_port_ready(Scheme_Object *p, Scheme_Schedule_Info *sinfo)
901 {
902 Scheme_Input_Port *ip;
903
904 ip = scheme_input_port_record(p);
905
906 if (ip->closed)
907 return 1;
908
909 if (SAME_OBJ(scheme_user_input_port_type, ip->sub_type)) {
910 /* We can't call the normal byte_ready because that runs Racket
911 code, and this function is called by the scheduler when
912 false_pos_ok is true. So, in that case, we assume that if the
913 port's evt is ready, then the port is ready. (After
914 all, false positives are ok in that mode.) Even when the
915 scheduler isn't requesting the status, we need sinfo. */
916 return scheme_user_port_byte_probably_ready(ip, sinfo);
917 } else
918 return byte_input_ready(p, sinfo);
919 }
920
register_port_wait()921 static void register_port_wait()
922 {
923 scheme_add_evt(scheme_input_port_type,
924 (Scheme_Ready_Fun)scheme_byte_ready_or_user_port_ready, scheme_need_wakeup,
925 evt_input_port_p, 1);
926 scheme_add_evt(scheme_output_port_type,
927 (Scheme_Ready_Fun)output_ready, output_need_wakeup,
928 evt_output_port_p, 1);
929 }
930
pipe_char_count(Scheme_Object * p)931 XFORM_NONGCING static int pipe_char_count(Scheme_Object *p)
932 {
933 if (p) {
934 Scheme_Pipe *pipe;
935 Scheme_Input_Port *ip;
936
937 ip = (Scheme_Input_Port *)p;
938 pipe = (Scheme_Pipe *)ip->port_data;
939
940 if (pipe->bufstart <= pipe->bufend)
941 return pipe->bufend - pipe->bufstart;
942 else
943 return (pipe->buflen - pipe->bufstart) + pipe->bufend;
944 } else
945 return 0;
946 }
947
scheme_pipe_char_count(Scheme_Object * p)948 int scheme_pipe_char_count(Scheme_Object *p)
949 {
950 return pipe_char_count(p);
951 }
952
953 /****************************** main input reader ******************************/
954
post_progress(Scheme_Input_Port * ip)955 static void post_progress(Scheme_Input_Port *ip)
956 {
957 scheme_post_sema_all(ip->progress_evt);
958 ip->progress_evt = NULL;
959 }
960
inc_pos(Scheme_Port * ip,int a)961 XFORM_NONGCING static void inc_pos(Scheme_Port *ip, int a)
962 {
963 if (ip->column >= 0)
964 ip->column += a;
965 if (ip->readpos >= 0)
966 ip->readpos += a;
967 ip->charsSinceNewline += a;
968 ip->utf8state = 0;
969 }
970
quick_plus(Scheme_Object * s,intptr_t v)971 static Scheme_Object *quick_plus(Scheme_Object *s, intptr_t v)
972 {
973 if (SCHEME_INTP(s)) {
974 int k;
975 k = SCHEME_INT_VAL(s);
976 if ((k < 0x1000000) && (v < 0x1000000)) {
977 k += v;
978 return scheme_make_integer(k);
979 }
980 }
981
982 /* Generic addition, but we might not be in a position to allow
983 thread swaps */
984 scheme_start_atomic();
985 s = scheme_bin_plus(s, scheme_make_integer(v));
986 scheme_end_atomic_no_swap();
987
988 return s;
989 }
990
991 #define state_len(state) ((state >> 3) & 0x7)
992
do_count_lines(Scheme_Port * ip,const char * buffer,intptr_t offset,intptr_t got)993 XFORM_NONGCING static void do_count_lines(Scheme_Port *ip, const char *buffer, intptr_t offset, intptr_t got)
994 {
995 intptr_t i;
996 int c, degot = 0;
997
998 ip->oldColumn = ip->column; /* works for a single-char read, like `read' */
999
1000 if (ip->readpos >= 0)
1001 ip->readpos += got; /* add for CR LF below */
1002
1003 /* Find start of last line: */
1004 for (i = got, c = 0; i--; c++) {
1005 if (buffer[offset + i] == '\n' || buffer[offset + i] == '\r') {
1006 break;
1007 }
1008 }
1009
1010 /* Count UTF-8-decoded chars, up to last line: */
1011 if (i >= 0) {
1012 int state = ip->utf8state;
1013 int n;
1014 degot += state_len(state);
1015 n = scheme_utf8_decode_count((const unsigned char *)buffer, offset, offset + i + 1, &state, 0, 0xFFFD);
1016 degot += (i + 1 - n);
1017 ip->utf8state = 0; /* assert: state == 0, because we ended with a newline */
1018 }
1019
1020 if (i >= 0) {
1021 int n = 0;
1022 ip->charsSinceNewline = c + 1;
1023 i++;
1024 /* Continue walking, back over the previous lines, to find
1025 out how many there were: */
1026 while (i--) {
1027 if (buffer[offset + i] == '\n') {
1028 if (!(i && (buffer[offset + i - 1] == '\r'))
1029 && !(!i && ip->was_cr)) {
1030 n++;
1031 } else
1032 degot++; /* adjust positions for CRLF -> LF conversion */
1033 } else if (buffer[offset + i] == '\r') {
1034 n++;
1035 }
1036 }
1037
1038 mzAssert(n > 0);
1039 if (ip->lineNumber >= 0)
1040 ip->lineNumber += n;
1041 ip->was_cr = (buffer[offset + got - 1] == '\r');
1042 /* Now reset column to 0: */
1043 if (ip->column >= 0)
1044 ip->column = 0;
1045 } else {
1046 ip->charsSinceNewline += c;
1047 ip->was_cr = 0;
1048 }
1049
1050 /* Do the last line to get the column count right and to
1051 further adjust positions for UTF-8 decoding: */
1052 {
1053 int col = ip->column, n;
1054 int prev_i = got - c;
1055 int state = ip->utf8state;
1056 n = state_len(state);
1057 degot += n;
1058 col -= n;
1059 for (i = prev_i; i < got; i++) {
1060 if (buffer[offset + i] == '\t') {
1061 n = scheme_utf8_decode_count((const unsigned char *)buffer, offset + prev_i, offset + i, &state, 0, 0xFFFD);
1062 degot += ((i - prev_i) - n);
1063 col += n;
1064 col = col - (col & 0x7) + 8;
1065 prev_i = i + 1;
1066 }
1067 }
1068 if (prev_i < i) {
1069 n = scheme_utf8_decode_count((const unsigned char *)buffer, offset + prev_i, offset + i, &state, 1, 0xFFFD);
1070 n += state_len(state);
1071 col += n;
1072 degot += ((i - prev_i) - n);
1073 }
1074 if (ip->column >= 0)
1075 ip->column = col;
1076 ip->utf8state = state;
1077 }
1078
1079 if (ip->readpos >= 0)
1080 ip->readpos -= degot;
1081 }
1082
scheme_port_count_lines(Scheme_Port * ip,const char * buffer,intptr_t offset,intptr_t got)1083 void scheme_port_count_lines(Scheme_Port *ip, const char *buffer, intptr_t offset, intptr_t got)
1084 {
1085 if (ip->position >= 0)
1086 ip->position += got;
1087
1088 if (ip->count_lines)
1089 do_count_lines(ip, buffer, offset, got);
1090 }
1091
scheme_get_byte_string_unless(const char * who,Scheme_Object * port,char * buffer,intptr_t offset,intptr_t size,int only_avail,int peek,Scheme_Object * peek_skip,Scheme_Object * unless_evt)1092 intptr_t scheme_get_byte_string_unless(const char *who,
1093 Scheme_Object *port,
1094 char *buffer, intptr_t offset, intptr_t size,
1095 int only_avail,
1096 int peek, Scheme_Object *peek_skip,
1097 Scheme_Object *unless_evt)
1098 {
1099 Scheme_Input_Port *ip;
1100 intptr_t got = 0, total_got = 0, gc;
1101 int special_ok = special_is_ok, check_special;
1102 Scheme_Get_String_Fun gs;
1103 Scheme_Peek_String_Fun ps;
1104
1105 /* See also get_one_byte, below. Any change to this function
1106 may require a change to 1-byte specialization of get_one_byte. */
1107
1108 /* back-door argument: */
1109 special_is_ok = 0;
1110
1111 if (!size) {
1112 if (only_avail == -1) {
1113 /* We might need to break. */
1114 if (scheme_current_thread->external_break) {
1115 scheme_thread_block_enable_break(0.0, 1);
1116 scheme_current_thread->ran_some = 1;
1117 }
1118 }
1119 return 0;
1120 }
1121 if (!peek_skip)
1122 peek_skip = scheme_make_integer(0);
1123
1124 ip = scheme_input_port_record(port);
1125
1126 gs = ip->get_string_fun;
1127 ps = ip->peek_string_fun;
1128
1129 while (1) {
1130 SCHEME_USE_FUEL(1);
1131
1132 if (ip->input_lock)
1133 scheme_wait_input_allowed(ip, only_avail);
1134
1135 /* check progress evt before checking for closed: */
1136 if (unless_evt
1137 && SAME_TYPE(SCHEME_TYPE(unless_evt), scheme_progress_evt_type)
1138 && SCHEME_SEMAP(SCHEME_PTR2_VAL(unless_evt))
1139 && scheme_try_plain_sema(SCHEME_PTR2_VAL(unless_evt)))
1140 return 0;
1141
1142 CHECK_PORT_CLOSED(who, "input", port, ip->closed);
1143
1144 if (only_avail == -1) {
1145 /* We might need to break. */
1146 if (scheme_current_thread->external_break) {
1147 scheme_thread_block_enable_break(0.0, 1);
1148 scheme_current_thread->ran_some = 1;
1149 }
1150 }
1151
1152 if ((ip->ungotten_count || pipe_char_count(ip->peeked_read))
1153 && (!total_got || !peek)) {
1154 intptr_t l, i;
1155 unsigned char *s;
1156
1157 i = ip->ungotten_count;
1158 /* s will be in reverse order */
1159
1160 if (peek) {
1161 if (!SCHEME_INTP(peek_skip) || (i < SCHEME_INT_VAL(peek_skip))) {
1162 peek_skip = scheme_bin_minus(peek_skip, scheme_make_integer(i));
1163 i = 0;
1164 } else {
1165 i -= SCHEME_INT_VAL(peek_skip);
1166 peek_skip = scheme_make_integer(0);
1167 }
1168 }
1169
1170 if (i < size)
1171 l = i;
1172 else
1173 l = size;
1174
1175 size -= l;
1176 s = (unsigned char *)ip->ungotten; /* Not GC-safe! */
1177 while (l--) {
1178 buffer[offset + got++] = s[--i];
1179 }
1180 s = NULL;
1181
1182 if (!peek) {
1183 ip->ungotten_count = i;
1184 ip->slow = 1;
1185 }
1186
1187 l = pipe_char_count(ip->peeked_read);
1188 if (size && l) {
1189 if (SCHEME_INTP(peek_skip) && (l > SCHEME_INT_VAL(peek_skip))) {
1190 l -= SCHEME_INT_VAL(peek_skip);
1191
1192 if (l > size)
1193 l = size;
1194
1195 if (l) {
1196 scheme_get_byte_string("depipe", ip->peeked_read,
1197 buffer, offset + got, l,
1198 1, peek, peek_skip);
1199 size -= l;
1200 got += l;
1201 peek_skip = scheme_make_integer(0);
1202 if (!peek && ip->progress_evt)
1203 post_progress(ip);
1204 }
1205 } else
1206 peek_skip = scheme_bin_minus(peek_skip, scheme_make_integer(l));
1207 }
1208 check_special = (!got || peek);
1209 } else
1210 check_special = 1;
1211
1212 if (check_special && ip->ungotten_special) {
1213 if (!special_ok) {
1214 if (!peek) {
1215 if (ip->progress_evt)
1216 post_progress(ip);
1217 ip->ungotten_special = NULL;
1218 }
1219 scheme_bad_time_for_special(who, port);
1220 }
1221 if (!peek) {
1222 ip->special = ip->ungotten_special;
1223 ip->ungotten_special = NULL;
1224 } else {
1225 if (peek_skip != scheme_make_integer(0))
1226 scheme_bad_time_for_special(who, port);
1227 }
1228
1229 if (!peek) {
1230 if (ip->p.position >= 0)
1231 ip->p.position++;
1232 if (ip->p.count_lines)
1233 inc_pos((Scheme_Port *)ip, 1);
1234 }
1235
1236 if (!peek && ip->progress_evt)
1237 post_progress(ip);
1238
1239 return SCHEME_SPECIAL;
1240 }
1241
1242 if (got && ((only_avail == 1) || (only_avail == -1)))
1243 only_avail = 2;
1244
1245 /* If we get this far in peek mode, ps is NULL, peek_skip is non-zero, and
1246 we haven't gotten anything so far, it means that we need to read before we
1247 can actually peek. Handle this case with a recursive peek that starts
1248 from the current position, then set peek_skip to 0 and go on. */
1249 while (peek && !ps && (peek_skip != scheme_make_integer(0)) && !total_got && !got
1250 && (ip->pending_eof < 2)) {
1251 char *tmp;
1252 int v, pcc;
1253 intptr_t skip;
1254 Scheme_Cont_Frame_Data cframe;
1255
1256
1257 # define MAX_SKIP_TRY_AMOUNT 65536
1258
1259 if (SCHEME_INTP(peek_skip)) {
1260 skip = SCHEME_INT_VAL(peek_skip);
1261 if (skip > MAX_SKIP_TRY_AMOUNT)
1262 skip = MAX_SKIP_TRY_AMOUNT;
1263 } else
1264 skip = MAX_SKIP_TRY_AMOUNT;
1265
1266 tmp = (char *)scheme_malloc_atomic(skip);
1267 pcc = pipe_char_count(ip->peeked_read);
1268
1269 if (only_avail == -1) {
1270 /* To implement .../enable-break, we enable
1271 breaks during the skip-ahead peek. */
1272 scheme_push_break_enable(&cframe, 1, 1);
1273 }
1274
1275 v = scheme_get_byte_string_unless(who, port, tmp, 0, skip,
1276 (only_avail == 2) ? 2 : 0,
1277 1, scheme_make_integer(ip->ungotten_count + pcc),
1278 unless_evt);
1279
1280 if (only_avail == -1) {
1281 scheme_pop_break_enable(&cframe, 0);
1282 }
1283
1284 if (v == EOF) {
1285 ip->p.utf8state = 0;
1286 return EOF;
1287 } else if (v == SCHEME_SPECIAL) {
1288 ip->special = NULL;
1289 scheme_bad_time_for_special(who, port);
1290 } else if (v > 0) {
1291 peek_skip = scheme_bin_minus(peek_skip, scheme_make_integer(skip));
1292 /* Ok... ready to continue (if skip == peek_skip) */
1293 } else {
1294 /* This shouldn't happen, but just in case */
1295 return 0;
1296 }
1297 }
1298
1299 if (size) {
1300 int nonblock;
1301
1302 if (only_avail == 2) {
1303 if (got)
1304 nonblock = 2;
1305 else
1306 nonblock = 1;
1307 } else if (only_avail == -1)
1308 nonblock = -1;
1309 else
1310 nonblock = 0;
1311
1312 if (unless_evt && SAME_TYPE(SCHEME_TYPE(unless_evt), scheme_progress_evt_type))
1313 unless_evt = SCHEME_PTR2_VAL(unless_evt);
1314
1315 if (ip->pending_eof > 1) {
1316 if (!peek) {
1317 ip->pending_eof = 1;
1318 if (ip->progress_evt)
1319 post_progress(ip);
1320 }
1321 gc = EOF;
1322 } else {
1323 /* Call port's get or peek function. But first, set up
1324 an "unless" to detect other accesses of the port
1325 if we block. */
1326 Scheme_Object *unless;
1327
1328 if (nonblock > 0) {
1329 if (ip->unless)
1330 unless = ip->unless;
1331 else
1332 unless = NULL;
1333 } else if (ip->unless_cache) {
1334 if (ip->unless) {
1335 unless = ip->unless;
1336 /* Setting car to #f means that it can't be recycled */
1337 SCHEME_CAR(unless) = scheme_false;
1338 } else if (SCHEME_TRUEP(ip->unless_cache)) {
1339 unless = ip->unless_cache;
1340 ip->unless_cache = scheme_false;
1341 ip->unless = unless;
1342 } else {
1343 unless = scheme_make_raw_pair(NULL, NULL);
1344 ip->unless = unless;
1345 }
1346 if (unless_evt)
1347 SCHEME_CDR(unless) = unless_evt;
1348 } else
1349 unless = unless_evt;
1350
1351 /* Finally, call port's get or peek: */
1352 if (peek && ps)
1353 gc = ps(ip, buffer, offset + got, size, peek_skip, nonblock, unless);
1354 else {
1355 gc = gs(ip, buffer, offset + got, size, nonblock, unless);
1356
1357 if (!peek && gc && ip->progress_evt
1358 && ((gc != EOF) || ip->pending_eof)
1359 && (gc != SCHEME_UNLESS_READY))
1360 post_progress(ip);
1361 }
1362
1363 /* Let other threads know that something happened,
1364 and/or deregister this thread's request for information. */
1365 if (unless && ip->unless_cache) {
1366 if (!SCHEME_CAR(unless)) {
1367 /* Recycle "unless", since we were the only user */
1368 ip->unless_cache = unless;
1369 SCHEME_CDR(unless) = NULL;
1370 } else {
1371 if (SCHEME_TRUEP(SCHEME_CAR(unless))) {
1372 /* gc should be SCHEME_UNLESS_READY; only a user
1373 port without a peek can incorrectly produce something
1374 else */
1375 if (gc == SCHEME_UNLESS_READY) {
1376 gc = 0;
1377 }
1378 } else if (gc) {
1379 /* Notify other threads that something happened */
1380 SCHEME_CAR(unless) = scheme_true;
1381 }
1382 }
1383 ip->unless = NULL;
1384 }
1385 }
1386
1387 if (gc == SCHEME_SPECIAL) {
1388 if (!got && !total_got && special_ok) {
1389 if (!peek) {
1390 if (ip->p.position >= 0)
1391 ip->p.position++;
1392 if (ip->p.count_lines)
1393 inc_pos((Scheme_Port *)ip, 1);
1394 }
1395
1396 return SCHEME_SPECIAL;
1397 }
1398
1399 if ((got || total_got) && only_avail) {
1400 ip->slow = 1;
1401 ip->ungotten_special = ip->special;
1402 ip->special = NULL;
1403 gc = 0;
1404 } else {
1405 ip->special = NULL;
1406 scheme_bad_time_for_special(who, port);
1407 return 0;
1408 }
1409 } else if (gc == EOF) {
1410 ip->p.utf8state = 0;
1411 if (!got && !total_got) {
1412 if (peek && ip->pending_eof) {
1413 ip->pending_eof = 2;
1414 ip->slow = 1;
1415 }
1416 return EOF;
1417 }
1418 /* remember the EOF for next time */
1419 if (ip->pending_eof) {
1420 ip->pending_eof = 2;
1421 ip->slow = 1;
1422 }
1423 gc = 0;
1424 size = 0; /* so that we stop */
1425 } else if (gc == SCHEME_UNLESS_READY) {
1426 gc = 0;
1427 size = 0; /* so that we stop */
1428 }
1429 mzAssert(gc >= 0);
1430 } else
1431 gc = 0;
1432
1433 got += gc;
1434 if (peek)
1435 peek_skip = quick_plus(peek_skip, gc);
1436 size -= gc;
1437
1438 if (!peek) {
1439 /****************************************************/
1440 /* Adjust position information for chars got so far */
1441 /****************************************************/
1442
1443 /* We don't get here if SCHEME_SPECIAL is returned, so
1444 the positions are updated separately in the two
1445 returning places above. */
1446
1447 if (ip->p.position >= 0)
1448 ip->p.position += got;
1449 if (ip->p.count_lines)
1450 do_count_lines((Scheme_Port *)ip, buffer, offset, got);
1451 } else if (!ps) {
1452 /***************************************************/
1453 /* save newly peeked string for future peeks/reads */
1454 /***************************************************/
1455 if (gc) {
1456 ip->slow = 1;
1457 if ((gc == 1) && !ip->ungotten_count && !ip->peeked_write)
1458 ip->ungotten[ip->ungotten_count++] = buffer[offset];
1459 else {
1460 if (!ip->peeked_write) {
1461 Scheme_Object *rd, *wt;
1462 scheme_pipe(&rd, &wt);
1463 ip->peeked_read = rd;
1464 ip->peeked_write = wt;
1465 }
1466
1467 scheme_put_byte_string("peek", ip->peeked_write,
1468 buffer, offset + got - gc, gc, 0);
1469 }
1470 }
1471 }
1472
1473 offset += got;
1474 total_got += got;
1475 got = 0; /* for next round, if any */
1476
1477 if (!size
1478 || (total_got && ((only_avail == 1) || (only_avail == -1)))
1479 || (only_avail == 2))
1480 break;
1481
1482 /* Need to try to get more. */
1483 }
1484
1485 return total_got;
1486 }
1487
scheme_get_byte_string_special_ok_unless(const char * who,Scheme_Object * port,char * buffer,intptr_t offset,intptr_t size,int only_avail,int peek,Scheme_Object * peek_skip,Scheme_Object * unless_evt)1488 intptr_t scheme_get_byte_string_special_ok_unless(const char *who,
1489 Scheme_Object *port,
1490 char *buffer, intptr_t offset, intptr_t size,
1491 int only_avail,
1492 int peek, Scheme_Object *peek_skip,
1493 Scheme_Object *unless_evt)
1494 {
1495 special_is_ok = 1;
1496 return scheme_get_byte_string_unless(who, port, buffer, offset, size,
1497 only_avail, peek, peek_skip, unless_evt);
1498 }
1499
scheme_get_byte_string(const char * who,Scheme_Object * port,char * buffer,intptr_t offset,intptr_t size,int only_avail,int peek,Scheme_Object * peek_skip)1500 intptr_t scheme_get_byte_string(const char *who,
1501 Scheme_Object *port,
1502 char *buffer, intptr_t offset, intptr_t size,
1503 int only_avail,
1504 int peek, Scheme_Object *peek_skip)
1505 {
1506 return scheme_get_byte_string_unless(who, port,
1507 buffer, offset, size,
1508 only_avail,
1509 peek, peek_skip,
1510 NULL);
1511 }
1512
scheme_unless_ready(Scheme_Object * unless)1513 int scheme_unless_ready(Scheme_Object *unless)
1514 {
1515 if (!unless)
1516 return 0;
1517
1518 if (SCHEME_CAR(unless) && SCHEME_TRUEP(SCHEME_CAR(unless)))
1519 return 1;
1520
1521 if (SCHEME_CDR(unless))
1522 return scheme_try_plain_sema(SCHEME_CDR(unless));
1523
1524 return 0;
1525 }
1526
1527
scheme_wait_input_allowed(Scheme_Input_Port * ip,int nonblock)1528 void scheme_wait_input_allowed(Scheme_Input_Port *ip, int nonblock)
1529 {
1530 while (ip->input_lock) {
1531 scheme_post_sema_all(ip->input_giveup);
1532 scheme_wait_sema(ip->input_lock, nonblock ? -1 : 0);
1533 }
1534 }
1535
release_input_lock(Scheme_Input_Port * ip)1536 static void release_input_lock(Scheme_Input_Port *ip)
1537 {
1538 scheme_post_sema_all(ip->input_lock);
1539 ip->input_lock = NULL;
1540 ip->input_giveup = NULL;
1541
1542 if (scheme_current_thread->running & MZTHREAD_NEED_SUSPEND_CLEANUP)
1543 scheme_current_thread->running -= MZTHREAD_NEED_SUSPEND_CLEANUP;
1544 }
1545
elect_new_main(Scheme_Input_Port * ip)1546 static void elect_new_main(Scheme_Input_Port *ip)
1547 {
1548 if (ip->input_extras_ready) {
1549 scheme_post_sema_all(ip->input_extras_ready);
1550 ip->input_extras = NULL;
1551 ip->input_extras_ready = NULL;
1552 }
1553 }
1554
release_input_lock_and_elect_new_main(void * _ip)1555 static void release_input_lock_and_elect_new_main(void *_ip)
1556 {
1557 Scheme_Input_Port *ip;
1558
1559 ip = scheme_input_port_record(_ip);
1560
1561 release_input_lock(ip);
1562 elect_new_main(ip);
1563 }
1564
check_suspended()1565 static void check_suspended()
1566 {
1567 if (scheme_current_thread->running & MZTHREAD_USER_SUSPENDED)
1568 scheme_thread_block(0.0);
1569 }
1570
remove_extra(void * ip_v)1571 static void remove_extra(void *ip_v)
1572 {
1573 Scheme_Input_Port *ip;
1574 Scheme_Object *v = SCHEME_CDR(ip_v), *ll, *prev;
1575
1576 ip = scheme_input_port_record(SCHEME_CAR(ip_v));
1577
1578 prev = NULL;
1579 for (ll = ip->input_extras; ll; prev = ll, ll = SCHEME_CDR(ll)) {
1580 if (SAME_OBJ(ll, SCHEME_CDR(v))) {
1581 if (prev)
1582 SCHEME_CDR(prev) = SCHEME_CDR(ll);
1583 else
1584 ip->input_extras = SCHEME_CDR(ll);
1585 SCHEME_CDR(ll) = NULL;
1586 break;
1587 }
1588 }
1589
1590 /* Tell the main commit thread (if any) to reset */
1591 if (ip->input_giveup)
1592 scheme_post_sema_all(ip->input_giveup);
1593 }
1594
complete_peeked_read_via_get(Scheme_Input_Port * ip,intptr_t size)1595 static int complete_peeked_read_via_get(Scheme_Input_Port *ip,
1596 intptr_t size)
1597 {
1598 Scheme_Get_String_Fun gs;
1599 char *buf, _buf[16];
1600 int buf_size = 16;
1601 buf = _buf;
1602
1603 /* Target event is ready, so commit must succeed */
1604
1605 /* First remove ungotten_count chars */
1606 if (ip->ungotten_count) {
1607 int i, amt;
1608
1609 if (ip->ungotten_count > size) {
1610 amt = size;
1611 ip->ungotten_count -= size;
1612 } else {
1613 amt = ip->ungotten_count;
1614 size -= ip->ungotten_count;
1615 ip->ungotten_count = 0;
1616 }
1617
1618 if (ip->p.position >= 0)
1619 ip->p.position += amt;
1620 if (ip->p.count_lines) {
1621 if (buf_size < amt) {
1622 buf = scheme_malloc_atomic(amt);
1623 buf_size = amt;
1624 }
1625 for (i = 0; i < amt; i++) {
1626 buf[i] = ip->ungotten[ip->ungotten_count + amt - i - 1];
1627 }
1628 do_count_lines((Scheme_Port *)ip, buf, 0, amt);
1629 }
1630
1631 if (ip->progress_evt)
1632 post_progress(ip);
1633 }
1634
1635 if (size) {
1636 Scheme_Input_Port *pip;
1637
1638 if (ip->peek_string_fun) {
1639 /* If the port supplies its own peek, then we don't
1640 have peeked_r, so pass NULL as a buffer to the port's
1641 read proc. The read proc must not block. */
1642 gs = ip->get_string_fun;
1643 pip = ip;
1644 } else {
1645 /* Otherwise, peek was implemented through peeked_{w,r}: */
1646 if (ip->peeked_read) {
1647 int cnt;
1648 cnt = pipe_char_count(ip->peeked_read);
1649 if ((cnt < size) && (ip->pending_eof == 2)) {
1650 ip->pending_eof = 1;
1651 size--;
1652 }
1653 pip = (Scheme_Input_Port *)ip->peeked_read;
1654 gs = pip->get_string_fun;
1655 } else {
1656 if (ip->pending_eof == 2) {
1657 ip->pending_eof = 1;
1658 if (ip->progress_evt)
1659 post_progress(ip);
1660 }
1661 gs = NULL;
1662 pip = NULL;
1663 }
1664 }
1665
1666 if (gs && size) {
1667 if (ip->p.count_lines) {
1668 if (buf_size < size) {
1669 buf = scheme_malloc_atomic(size);
1670 buf_size = size;
1671 }
1672 } else
1673 buf = NULL;
1674 size = gs(pip, buf, 0, size, 1, NULL);
1675 if (size > 0) {
1676 if (ip->progress_evt)
1677 post_progress(ip);
1678 if (ip->p.position >= 0)
1679 ip->p.position += size;
1680 if (buf)
1681 do_count_lines((Scheme_Port *)ip, buf, 0, size);
1682 }
1683 }
1684 }
1685
1686 return 1;
1687 }
1688
return_data(void * data,int argc,Scheme_Object ** argv)1689 static Scheme_Object *return_data(void *data, int argc, Scheme_Object **argv)
1690 {
1691 return (Scheme_Object *)data;
1692 }
1693
scheme_peeked_read_via_get(Scheme_Input_Port * ip,intptr_t _size,Scheme_Object * unless_evt,Scheme_Object * _target_evt)1694 int scheme_peeked_read_via_get(Scheme_Input_Port *ip,
1695 intptr_t _size,
1696 Scheme_Object *unless_evt,
1697 Scheme_Object *_target_evt)
1698 {
1699 Scheme_Object * volatile v, *sema, *a[3], ** volatile aa, * volatile l;
1700 volatile intptr_t size = _size;
1701 volatile int n, current_leader = 0;
1702 volatile Scheme_Type t;
1703 Scheme_Object * volatile target_evt = _target_evt;
1704
1705 /* Check whether t's event value is known to be always itself: */
1706 t = SCHEME_TYPE(target_evt);
1707 if (!SAME_TYPE(t, scheme_sema_type)
1708 && !SAME_TYPE(t, scheme_channel_put_type)
1709 && !SAME_TYPE(t, scheme_always_evt_type)
1710 && !SAME_TYPE(t, scheme_never_evt_type)
1711 && !SAME_TYPE(t, scheme_semaphore_repost_type)) {
1712 /* Make an event whose value is itself */
1713 a[0] = target_evt;
1714 v = scheme_make_closed_prim(return_data, target_evt);
1715 a[1] = v;
1716 target_evt = scheme_wrap_evt(2, a);
1717 ((Scheme_Closed_Primitive_Proc *)v)->data = target_evt;
1718 }
1719
1720 /* This commit implementation is essentially CML style, but we avoid
1721 actually allocating a manager thread. Instead the various
1722 committing threads elect a leader, and we rely on being in the
1723 kernel to detect when the leader is killed or suspended, in which
1724 case we elect a new leader. */
1725
1726 while (1) {
1727 if (scheme_wait_sema(unless_evt, 1)) {
1728 if (current_leader)
1729 elect_new_main(ip);
1730 return 0;
1731 }
1732
1733 if (!current_leader && ip->input_giveup) {
1734 /* Some other thread is already trying to commit.
1735 Ask it to sync on our target, too */
1736 v = scheme_make_pair(scheme_make_integer(_size), target_evt);
1737 l = scheme_make_raw_pair(v, ip->input_extras);
1738 ip->input_extras = l;
1739
1740 scheme_post_sema_all(ip->input_giveup);
1741
1742 if (!ip->input_extras_ready) {
1743 sema = scheme_make_sema(0);
1744 ip->input_extras_ready = sema;
1745 }
1746
1747 a[0] = ip->input_extras_ready;
1748 l = scheme_make_pair((Scheme_Object *)ip, v);
1749 BEGIN_ESCAPEABLE(remove_extra, l);
1750 scheme_sync(1, a);
1751 END_ESCAPEABLE();
1752
1753 if (!SCHEME_CDR(v)) {
1754 /* We were selected, so the commit succeeded. */
1755 return SCHEME_TRUEP(SCHEME_CAR(v)) ? 1 : 0;
1756 }
1757 } else {
1758 /* No other thread is trying to commit. This one is hereby
1759 elected "main" if multiple threads try to commit. */
1760
1761 if (SAME_TYPE(t, scheme_always_evt_type)) {
1762 /* Fast path: always-evt is ready */
1763 return complete_peeked_read_via_get(ip, size);
1764 }
1765
1766 /* This sema makes other threads wait before reading: */
1767 sema = scheme_make_sema(0);
1768 ip->input_lock = sema;
1769 ip->slow = 1;
1770
1771 /* This sema lets other threads try to make progress,
1772 if the current target doesn't work out */
1773 sema = scheme_make_sema(0);
1774 ip->input_giveup = sema;
1775
1776 if (ip->input_extras) {
1777 /* There are other threads trying to commit, and
1778 as main thread, we'll help them out. */
1779 n = 3;
1780 for (l = ip->input_extras; l; l = SCHEME_CDR(l)) {
1781 n++;
1782 }
1783 aa = MALLOC_N(Scheme_Object *, n);
1784 n = 3;
1785 for (l = ip->input_extras; l; l = SCHEME_CDR(l)) {
1786 aa[n++] = SCHEME_CDR(SCHEME_CAR(l));
1787 }
1788 } else {
1789 /* This is the only thread trying to commit */
1790 n = 3;
1791 aa = a;
1792 }
1793
1794 /* Suspend here is a problem if another thread
1795 tries to commit, because this thread will be
1796 responsible for multiplexing the commits. That's
1797 why the thread waits on its own suspend event. */
1798
1799 aa[0] = target_evt;
1800 aa[1] = ip->input_giveup;
1801 v = scheme_get_thread_suspend(scheme_current_thread);
1802 aa[2] = v;
1803
1804 scheme_current_thread->running |= MZTHREAD_NEED_SUSPEND_CLEANUP;
1805 BEGIN_ESCAPEABLE(release_input_lock_and_elect_new_main, ip);
1806 v = scheme_sync(n, aa);
1807 END_ESCAPEABLE();
1808
1809 release_input_lock(ip);
1810
1811 if (SAME_OBJ(v, target_evt)) {
1812 int r;
1813 elect_new_main(ip);
1814 r = complete_peeked_read_via_get(ip, size);
1815 check_suspended();
1816 return r;
1817 }
1818
1819 if (n > 3) {
1820 /* Check whether one of the others was selected: */
1821 for (l = ip->input_extras; l; l = SCHEME_CDR(l)) {
1822 if (SAME_OBJ(v, SCHEME_CDR(SCHEME_CAR(l)))) {
1823 /* Yep. Clear the cdr to tell the relevant thread
1824 that it was selected, and reset the extras. */
1825 v = SCHEME_CAR(l);
1826 SCHEME_CDR(v) = NULL;
1827 size = SCHEME_INT_VAL(SCHEME_CAR(v));
1828 elect_new_main(ip);
1829 if (complete_peeked_read_via_get(ip, size))
1830 SCHEME_CAR(v) = scheme_true;
1831 else
1832 SCHEME_CAR(v) = scheme_false;
1833 check_suspended();
1834 return 0;
1835 }
1836 }
1837 }
1838
1839 if (scheme_current_thread->running & MZTHREAD_USER_SUSPENDED) {
1840 elect_new_main(ip);
1841 current_leader = 0;
1842 check_suspended();
1843 } else {
1844 current_leader = 1;
1845
1846 /* Technically redundant, but avoid a thread swap
1847 if we know the commit isn't going to work: */
1848 if (scheme_wait_sema(unless_evt, 1)) {
1849 elect_new_main(ip);
1850 return 0;
1851 }
1852
1853 scheme_thread_block(0.0);
1854 }
1855 }
1856 }
1857 }
1858
scheme_peeked_read(Scheme_Object * port,intptr_t size,Scheme_Object * unless_evt,Scheme_Object * target_evt)1859 int scheme_peeked_read(Scheme_Object *port,
1860 intptr_t size,
1861 Scheme_Object *unless_evt,
1862 Scheme_Object *target_evt)
1863 {
1864 Scheme_Input_Port *ip;
1865 Scheme_Peeked_Read_Fun pr;
1866
1867 ip = scheme_input_port_record(port);
1868
1869 unless_evt = SCHEME_PTR2_VAL(unless_evt);
1870
1871 pr = ip->peeked_read_fun;
1872
1873 return pr(ip, size, unless_evt, target_evt);
1874 }
1875
scheme_progress_evt_via_get(Scheme_Input_Port * port)1876 Scheme_Object *scheme_progress_evt_via_get(Scheme_Input_Port *port)
1877 {
1878 Scheme_Object *sema;
1879
1880 if (port->progress_evt)
1881 return port->progress_evt;
1882
1883 sema = scheme_make_sema(0);
1884
1885 if (port->closed) {
1886 scheme_post_sema_all(sema);
1887 return sema;
1888 }
1889
1890 port->progress_evt = sema;
1891 port->slow = 1;
1892
1893 return sema;
1894 }
1895
scheme_progress_evt(Scheme_Object * port)1896 Scheme_Object *scheme_progress_evt(Scheme_Object *port)
1897 {
1898 Scheme_Input_Port *ip;
1899
1900 ip = scheme_input_port_record(port);
1901
1902 if (ip->progress_evt_fun) {
1903 Scheme_Progress_Evt_Fun ce;
1904 Scheme_Object *evt, *o;
1905
1906 ce = ip->progress_evt_fun;
1907
1908 evt = ce(ip);
1909
1910 o = scheme_alloc_object();
1911 o->type = scheme_progress_evt_type;
1912 SCHEME_PTR1_VAL(o) = (Scheme_Object *)port;
1913 SCHEME_PTR2_VAL(o) = evt;
1914
1915 return o;
1916 }
1917
1918 return NULL;
1919 }
1920
progress_evt_ready(Scheme_Object * evt,Scheme_Schedule_Info * sinfo)1921 static int progress_evt_ready(Scheme_Object *evt, Scheme_Schedule_Info *sinfo)
1922 {
1923 scheme_set_sync_target(sinfo, SCHEME_PTR2_VAL(evt), evt, NULL, 0, 1, NULL);
1924 return 0;
1925 }
1926
closed_evt_ready(Scheme_Object * evt,Scheme_Schedule_Info * sinfo)1927 static int closed_evt_ready(Scheme_Object *evt, Scheme_Schedule_Info *sinfo)
1928 {
1929 scheme_set_sync_target(sinfo, SCHEME_PTR_VAL(evt), evt, NULL, 0, 1, NULL);
1930 return 0;
1931 }
1932
scheme_get_char_string(const char * who,Scheme_Object * port,mzchar * buffer,intptr_t offset,intptr_t size,int peek,Scheme_Object * peek_skip)1933 intptr_t scheme_get_char_string(const char *who,
1934 Scheme_Object *port,
1935 mzchar *buffer, intptr_t offset, intptr_t size,
1936 int peek, Scheme_Object *peek_skip)
1937 {
1938 int ahead_skip = 0;
1939 char *s;
1940 int total_got = 0, bsize, leftover = 0, got;
1941
1942 /* read_string_byte_buffer helps avoid allocation */
1943 if (read_string_byte_buffer) {
1944 s = read_string_byte_buffer;
1945 read_string_byte_buffer = NULL;
1946 } else
1947 s = (char *)scheme_malloc_atomic(READ_STRING_BYTE_BUFFER_SIZE);
1948
1949 while (1) {
1950 /* Since we want "size" more chars and we don't have leftovers, we
1951 need at least "size" more bytes.
1952
1953 "leftover" is the number of bytes (<< READ_STRING_BYTE_BUFFER_SIZE) that
1954 we already have toward the first character. If the next
1955 character doesn't continue a leftover sequence, the next
1956 character actually belongs to a (leftover+1)th character. Thus,
1957 if leftover is positive and we're not merely peeking, ask for
1958 at leat one byte, but otherwise no more than size - leftover
1959 bytes. If size is 1, then we are forced to peek in all cases.
1960
1961 Overall, if the size is big enough, we only read as many
1962 characters as our buffer holds. */
1963
1964 bsize = size;
1965 if (leftover) {
1966 bsize -= leftover;
1967 if (bsize < 1) {
1968 /* This is the complex case. Need to peek a byte to see
1969 whether it continues the leftover sequence or ends it an in
1970 an error. */
1971 if (!peek_skip)
1972 peek_skip = scheme_make_integer(0);
1973 special_is_ok = 1;
1974 got = scheme_get_byte_string_unless(who, port,
1975 s, leftover, 1,
1976 0, 1 /* => peek */,
1977 quick_plus(peek_skip, ahead_skip),
1978 NULL);
1979 if (got > 0) {
1980 intptr_t ulen, glen;
1981 glen = scheme_utf8_decode_as_prefix((const unsigned char *)s, 0, got + leftover,
1982 buffer, offset, offset + size,
1983 &ulen, 0, 0xFFFD);
1984 if (glen && (ulen < got + leftover)) {
1985 /* Got one, with a decoding error. If we weren't peeking,
1986 don't read the lookahead bytes after all, yet. */
1987 total_got++;
1988 bsize = 0;
1989 ahead_skip++;
1990 size--;
1991 offset++;
1992 /* leftover stays the same */
1993 memmove(s, s + 1, leftover);
1994 } else {
1995 /* Either we got one character, or we're still continuing. */
1996 ahead_skip++;
1997 if (!glen) {
1998 /* Continuing */
1999 leftover++;
2000 } else {
2001 /* Got one (no encoding error) */
2002 leftover = 0;
2003 offset++;
2004 --size;
2005 total_got++;
2006 if (!peek) {
2007 /* Read the lookahead bytes and discard them */
2008 scheme_get_byte_string_unless(who, port,
2009 s, 0, ahead_skip,
2010 0, 0, scheme_make_integer(0),
2011 NULL);
2012 } else {
2013 peek_skip = quick_plus(peek_skip, ahead_skip);
2014 }
2015 ahead_skip = 0;
2016 }
2017 /* Continue with the normal decoing process (but get 0
2018 more characters this time around) */
2019 bsize = 0;
2020 }
2021 } else {
2022 /* Either EOF or SPECIAL -- either one ends the leftover
2023 sequence in an error. We may have more leftover chars
2024 than we need, but they haven't been read, yet. */
2025 while (leftover && size) {
2026 buffer[offset++] = 0xFFFD;
2027 total_got++;
2028 --leftover;
2029 --size;
2030 }
2031 return total_got;
2032 }
2033 }
2034 }
2035
2036 if (bsize) {
2037 /* Read bsize bytes */
2038 if (bsize + leftover > READ_STRING_BYTE_BUFFER_SIZE)
2039 bsize = READ_STRING_BYTE_BUFFER_SIZE - leftover;
2040
2041 got = scheme_get_byte_string_unless(who, port,
2042 s, leftover, bsize,
2043 0, peek, peek_skip,
2044 NULL);
2045 } else
2046 got = 0;
2047
2048 if (got >= 0) {
2049 intptr_t ulen, glen;
2050
2051 glen = scheme_utf8_decode_as_prefix((const unsigned char *)s, 0, got + leftover,
2052 buffer, offset, offset + size,
2053 &ulen, 0, 0xFFFD);
2054
2055 total_got += glen;
2056 if (glen == size) {
2057 /* Got enough */
2058 read_string_byte_buffer = s;
2059 return total_got;
2060 }
2061 offset += glen;
2062 size -= glen;
2063 leftover = (got + leftover) - ulen;
2064 memmove(s, s + ulen, leftover);
2065 if (peek) {
2066 peek_skip = quick_plus(peek_skip, got);
2067 }
2068 } else {
2069 read_string_byte_buffer = s;
2070
2071 /* Leftover bytes must be decoding-error bytes: */
2072 while (leftover) {
2073 buffer[offset++] = 0xFFFD;
2074 total_got++;
2075 --leftover;
2076 }
2077
2078 if (!total_got)
2079 return got; /* must be EOF */
2080 else
2081 return total_got;
2082 }
2083 }
2084 }
2085
2086 MZ_DO_NOT_INLINE(static intptr_t get_one_byte_slow(const char *who,
2087 Scheme_Object *port,
2088 char *buffer, intptr_t offset,
2089 int only_avail));
2090
get_one_byte_slow(const char * who,Scheme_Object * port,char * buffer,intptr_t offset,int only_avail)2091 static intptr_t get_one_byte_slow(const char *who,
2092 Scheme_Object *port,
2093 char *buffer, intptr_t offset,
2094 int only_avail)
2095 {
2096 Scheme_Input_Port *ip;
2097 intptr_t gc;
2098 int special_ok = special_is_ok;
2099 Scheme_Get_String_Fun gs;
2100
2101 special_is_ok = 0;
2102
2103 ip = scheme_input_port_record(port);
2104
2105 CHECK_PORT_CLOSED(who, "input", port, ip->closed);
2106
2107 if (ip->input_lock)
2108 scheme_wait_input_allowed(ip, only_avail);
2109
2110 if (ip->ungotten_count) {
2111 buffer[offset] = ip->ungotten[--ip->ungotten_count];
2112 gc = 1;
2113 } else if (ip->peeked_read && pipe_char_count(ip->peeked_read)) {
2114 int ch;
2115 ch = scheme_get_byte(ip->peeked_read);
2116 buffer[offset] = ch;
2117 gc = 1;
2118 } else if (ip->ungotten_special) {
2119 if (ip->progress_evt)
2120 post_progress(ip);
2121 if (!special_ok) {
2122 ip->ungotten_special = NULL;
2123 scheme_bad_time_for_special(who, port);
2124 return 0;
2125 }
2126 ip->special = ip->ungotten_special;
2127 ip->ungotten_special = NULL;
2128 if (ip->p.position >= 0)
2129 ip->p.position++;
2130 if (ip->p.count_lines)
2131 inc_pos((Scheme_Port *)ip, 1);
2132 return SCHEME_SPECIAL;
2133 } else {
2134 if (ip->pending_eof > 1) {
2135 ip->pending_eof = 1;
2136 return EOF;
2137 } else {
2138 if (!ip->progress_evt && !ip->p.count_lines)
2139 ip->slow = 0;
2140
2141 /* Call port's get function. */
2142 gs = ip->get_string_fun;
2143
2144 gc = gs(ip, buffer, offset, 1, 0, NULL);
2145
2146 if (ip->progress_evt && (gc > 0))
2147 post_progress(ip);
2148
2149 if (gc < 1) {
2150 if (gc == SCHEME_SPECIAL) {
2151 if (special_ok) {
2152 if (ip->p.position >= 0)
2153 ip->p.position++;
2154 if (ip->p.count_lines)
2155 inc_pos((Scheme_Port *)ip, 1);
2156 return SCHEME_SPECIAL;
2157 } else {
2158 scheme_bad_time_for_special(who, port);
2159 return 0;
2160 }
2161 } else if (gc == EOF) {
2162 ip->p.utf8state = 0;
2163 return EOF;
2164 } else {
2165 /* didn't get anything the first try, so use slow path: */
2166 special_is_ok = special_ok;
2167 return scheme_get_byte_string_unless(who, port,
2168 buffer, offset, 1,
2169 0, 0, NULL, NULL);
2170 }
2171 }
2172 }
2173 }
2174
2175 /****************************************************/
2176 /* Adjust position information for chars got so far */
2177 /****************************************************/
2178
2179 if (ip->p.position >= 0)
2180 ip->p.position++;
2181 if (ip->p.count_lines)
2182 do_count_lines((Scheme_Port *)ip, buffer, offset, 1);
2183
2184 return gc;
2185 }
2186
get_one_byte(GC_CAN_IGNORE const char * who,Scheme_Object * port,char * buffer)2187 static MZ_INLINE intptr_t get_one_byte(GC_CAN_IGNORE const char *who,
2188 Scheme_Object *port, char *buffer)
2189 {
2190 if (!special_is_ok && SCHEME_INPORTP(port)) {
2191 GC_CAN_IGNORE Scheme_Input_Port *ip;
2192 ip = (Scheme_Input_Port *)port;
2193 if (!ip->slow) {
2194 Scheme_Get_String_Fun gs;
2195 intptr_t v;
2196
2197 gs = ip->get_string_fun;
2198
2199 v = gs(ip, buffer, 0, 1, 0, NULL);
2200
2201 if (v) {
2202 if (v == SCHEME_SPECIAL) {
2203 scheme_bad_time_for_special(who, port);
2204 }
2205 if (v != EOF) {
2206 ip = (Scheme_Input_Port *)port; /* since `ip is ignored by GC */
2207 if (ip->p.position >= 0)
2208 ip->p.position++;
2209 }
2210
2211 return v;
2212 }
2213 }
2214 }
2215
2216 return get_one_byte_slow(who, port, buffer, 0, 0);
2217 }
2218
scheme_getc(Scheme_Object * port)2219 int scheme_getc(Scheme_Object *port)
2220 {
2221 char s[MAX_UTF8_CHAR_BYTES];
2222 unsigned int r[1];
2223 int v, delta = 0;
2224
2225 while(1) {
2226 if (delta) {
2227 v = scheme_get_byte_string_unless("read-char", port,
2228 s, delta, 1,
2229 0,
2230 delta > 0, scheme_make_integer(delta-1),
2231 NULL);
2232 } else {
2233 v = get_one_byte("read-char", port, s);
2234 }
2235
2236 if ((v == EOF) || (v == SCHEME_SPECIAL)) {
2237 if (!delta)
2238 return v;
2239 else {
2240 /* This counts as a decoding error. The high bit
2241 on the first character must be set. */
2242 return 0xFFFD;
2243 }
2244 } else {
2245 v = scheme_utf8_decode_prefix((const unsigned char *)s, delta + 1, r, 0);
2246 if (v > 0) {
2247 if (delta) {
2248 /* Need to read the peeked bytes (will ignore) */
2249 v = scheme_get_byte_string_unless("read-char", port,
2250 s, 0, delta,
2251 0,
2252 0, 0,
2253 NULL);
2254 }
2255 return r[0];
2256 } else if (v == -2) {
2257 /* -2 => decoding error */
2258 return 0xFFFD;
2259 } else if (v == -1) {
2260 /* In middle of sequence; start/continue peeking bytes */
2261 delta++;
2262 }
2263 }
2264 }
2265 }
2266
2267 int
scheme_get_byte(Scheme_Object * port)2268 scheme_get_byte(Scheme_Object *port) XFORM_ASSERT_NO_CONVERSION
2269 {
2270 char s[1];
2271 int v;
2272
2273 v = get_one_byte("read-byte", port, s);
2274
2275 if ((v == EOF) || (v == SCHEME_SPECIAL))
2276 return v;
2277 else
2278 return ((unsigned char *)s)[0];
2279 }
2280
2281 int
scheme_getc_special_ok(Scheme_Object * port)2282 scheme_getc_special_ok(Scheme_Object *port)
2283 {
2284 special_is_ok = 1;
2285 return scheme_getc(port);
2286 }
2287
2288 int
scheme_get_byte_special_ok(Scheme_Object * port)2289 scheme_get_byte_special_ok(Scheme_Object *port)
2290 {
2291 special_is_ok = 1;
2292 return scheme_get_byte(port);
2293 }
2294
scheme_get_bytes(Scheme_Object * port,intptr_t size,char * buffer,int offset)2295 intptr_t scheme_get_bytes(Scheme_Object *port, intptr_t size, char *buffer, int offset)
2296 {
2297 int n;
2298 int only_avail = 0;
2299
2300 if (size < 0) {
2301 size = -size;
2302 only_avail = 1;
2303 }
2304
2305 n = scheme_get_byte_string_unless("read-bytes", port,
2306 buffer, offset, size,
2307 only_avail,
2308 0, 0,
2309 NULL);
2310
2311 if (n == EOF)
2312 n = 0;
2313
2314 mzAssert(n >= 0);
2315
2316 return n;
2317 }
2318
scheme_peek_byte_skip(Scheme_Object * port,Scheme_Object * skip,Scheme_Object * unless_evt)2319 int scheme_peek_byte_skip(Scheme_Object *port, Scheme_Object *skip, Scheme_Object *unless_evt)
2320 {
2321 char s[1];
2322 int v;
2323
2324 v = scheme_get_byte_string_unless("peek-byte", port,
2325 s, 0, 1,
2326 0,
2327 1, skip,
2328 unless_evt);
2329
2330 if ((v == EOF) || (v == SCHEME_SPECIAL))
2331 return v;
2332 else
2333 return ((unsigned char *)s)[0];
2334 }
2335
scheme_peek_byte(Scheme_Object * port)2336 int scheme_peek_byte(Scheme_Object *port)
2337 {
2338 return scheme_peek_byte_skip(port, NULL, NULL);
2339 }
2340
2341 int
scheme_peek_byte_special_ok_skip(Scheme_Object * port,Scheme_Object * skip,Scheme_Object * unless_evt)2342 scheme_peek_byte_special_ok_skip(Scheme_Object *port, Scheme_Object *skip, Scheme_Object *unless_evt)
2343 {
2344 special_is_ok = 1;
2345 return scheme_peek_byte_skip(port, skip, unless_evt);
2346 }
2347
do_peekc_skip(Scheme_Object * port,Scheme_Object * skip,int only_avail,int * unavail)2348 static int do_peekc_skip(Scheme_Object *port, Scheme_Object *skip,
2349 int only_avail, int *unavail)
2350 {
2351 char s[MAX_UTF8_CHAR_BYTES];
2352 unsigned int r[1];
2353 int v, delta = 0;
2354 Scheme_Object *skip2;
2355
2356 if (unavail)
2357 *unavail = 0;
2358
2359 while(1) {
2360 if (delta) {
2361 if (!skip)
2362 skip = scheme_make_integer(0);
2363 skip2 = quick_plus(skip, delta);
2364 } else
2365 skip2 = skip;
2366
2367 v = scheme_get_byte_string_unless("peek-char", port,
2368 s, delta, 1,
2369 only_avail,
2370 1, skip2,
2371 NULL);
2372
2373 if (!v) {
2374 if (unavail)
2375 *unavail = 1;
2376 return 0;
2377 }
2378
2379 if ((v == EOF) || (v == SCHEME_SPECIAL)) {
2380 if (!delta)
2381 return v;
2382 else {
2383 /* This counts as a decoding error, so return 0xFFFD */
2384 return 0xFFFD;
2385 }
2386 } else {
2387 v = scheme_utf8_decode_prefix((const unsigned char *)s, delta + 1, r, 0);
2388 if (v > 0)
2389 return r[0];
2390 else if (v == -2) {
2391 /* -2 => decoding error */
2392 return 0xFFFD;
2393 } else if (v == -1) {
2394 /* In middle of sequence - keep getting bytes. */
2395 delta++;
2396 }
2397 }
2398 }
2399 }
2400
scheme_peekc_skip(Scheme_Object * port,Scheme_Object * skip)2401 int scheme_peekc_skip(Scheme_Object *port, Scheme_Object *skip)
2402 {
2403 return do_peekc_skip(port, skip, 0, NULL);
2404 }
2405
scheme_peekc(Scheme_Object * port)2406 int scheme_peekc(Scheme_Object *port)
2407 {
2408 return scheme_peekc_skip(port, scheme_make_integer(0));
2409 }
2410
2411 int
scheme_peekc_special_ok_skip(Scheme_Object * port,Scheme_Object * skip)2412 scheme_peekc_special_ok_skip(Scheme_Object *port, Scheme_Object *skip)
2413 {
2414 special_is_ok = 1;
2415 return scheme_peekc_skip(port, skip);
2416 }
2417
2418 int
scheme_peekc_special_ok(Scheme_Object * port)2419 scheme_peekc_special_ok(Scheme_Object *port)
2420 {
2421 return scheme_peekc_special_ok_skip(port, scheme_make_integer(0));
2422 }
2423
scheme_peekc_is_ungetc(Scheme_Object * port)2424 int scheme_peekc_is_ungetc(Scheme_Object *port)
2425 {
2426 Scheme_Input_Port *ip;
2427
2428 ip = scheme_input_port_record(port);
2429
2430 return !ip->peek_string_fun;
2431 }
2432
make_read_write_evt(Scheme_Type type,Scheme_Object * port,Scheme_Object * skip,char * str,intptr_t start,intptr_t size)2433 Scheme_Object *make_read_write_evt(Scheme_Type type,
2434 Scheme_Object *port, Scheme_Object *skip,
2435 char *str, intptr_t start, intptr_t size)
2436 {
2437 Scheme_Read_Write_Evt *rww;
2438
2439 rww = MALLOC_ONE_TAGGED(Scheme_Read_Write_Evt);
2440 rww->so.type = type;
2441 rww->port = port;
2442 rww->v = skip;
2443 rww->str = str;
2444 rww->start = start;
2445 rww->size = size;
2446
2447 return (Scheme_Object *)rww;
2448 }
2449
rw_evt_ready(Scheme_Object * _rww,Scheme_Schedule_Info * sinfo)2450 static int rw_evt_ready(Scheme_Object *_rww, Scheme_Schedule_Info *sinfo)
2451 {
2452 Scheme_Read_Write_Evt *rww = (Scheme_Read_Write_Evt *)_rww;
2453 intptr_t v;
2454
2455 if (sinfo->false_positive_ok) {
2456 /* Causes the thread to swap in, which we need in case there's an
2457 exception: */
2458 sinfo->potentially_false_positive = 1;
2459 return 1;
2460 }
2461
2462 if (rww->v) {
2463 Scheme_Output_Port *op;
2464 Scheme_Write_Special_Fun ws;
2465
2466 op = scheme_output_port_record(rww->port);
2467 ws = op->write_special_fun;
2468
2469 v = ws(op, rww->v, 1);
2470 if (v) {
2471 scheme_set_sync_target(sinfo, scheme_true, NULL, NULL, 0, 0, NULL);
2472 return 1;
2473 } else
2474 return 0;
2475 } else {
2476 v = scheme_put_byte_string("write-evt", rww->port,
2477 rww->str, rww->start, rww->size,
2478 2);
2479 if (v < 1)
2480 return 0;
2481 else if (!v && rww->size)
2482 return 0;
2483 else {
2484 scheme_set_sync_target(sinfo, scheme_make_integer(v), NULL, NULL, 0, 0, NULL);
2485 return 1;
2486 }
2487 }
2488 }
2489
rw_evt_wakeup(Scheme_Object * _rww,void * fds)2490 static void rw_evt_wakeup(Scheme_Object *_rww, void *fds)
2491 {
2492 Scheme_Read_Write_Evt *rww = (Scheme_Read_Write_Evt *)_rww;
2493
2494 if (rww->port) {
2495 if (rww->so.type == scheme_write_evt_type)
2496 output_need_wakeup(rww->port, fds);
2497 else
2498 scheme_need_wakeup(rww->port, fds);
2499 }
2500 }
2501
scheme_write_evt_via_write(Scheme_Output_Port * port,const char * str,intptr_t offset,intptr_t size)2502 Scheme_Object *scheme_write_evt_via_write(Scheme_Output_Port *port,
2503 const char *str, intptr_t offset, intptr_t size)
2504 {
2505 return make_read_write_evt(scheme_write_evt_type, (Scheme_Object *)port, NULL,
2506 (char *)str, offset, size);
2507 }
2508
scheme_write_special_evt_via_write_special(Scheme_Output_Port * port,Scheme_Object * special)2509 Scheme_Object *scheme_write_special_evt_via_write_special(Scheme_Output_Port *port,
2510 Scheme_Object *special)
2511 {
2512 return make_read_write_evt(scheme_write_evt_type, (Scheme_Object *)port, special,
2513 NULL, 0, 1);
2514 }
2515
scheme_make_write_evt(const char * who,Scheme_Object * port,Scheme_Object * special,char * str,intptr_t start,intptr_t size)2516 Scheme_Object *scheme_make_write_evt(const char *who, Scheme_Object *port,
2517 Scheme_Object *special, char *str, intptr_t start, intptr_t size)
2518 {
2519 Scheme_Output_Port *op;
2520
2521 op = scheme_output_port_record(port);
2522
2523 if (!special) {
2524 if (op->write_string_evt_fun) {
2525 Scheme_Write_String_Evt_Fun wse;
2526 wse = op->write_string_evt_fun;
2527 return wse(op, str, start, size);
2528 }
2529 } else {
2530 if (op->write_special_evt_fun) {
2531 Scheme_Write_Special_Evt_Fun wse = op->write_special_evt_fun;
2532 return wse(op, special);
2533 }
2534 }
2535
2536 scheme_contract_error("write-bytes-avail-evt",
2537 "port does not support atomic writes",
2538 "port", 1, port,
2539 NULL);
2540 return NULL;
2541 }
2542
2543 void
scheme_ungetc(int ch,Scheme_Object * port)2544 scheme_ungetc (int ch, Scheme_Object *port)
2545 {
2546 Scheme_Input_Port *ip;
2547
2548 ip = scheme_input_port_record(port);
2549
2550 CHECK_PORT_CLOSED("#<primitive:peek-port-char>", "input", port, ip->closed);
2551
2552 ip->slow = 1;
2553
2554 if (ch == EOF) {
2555 if (ip->pending_eof) /* non-zero means that EOFs are tracked */
2556 ip->pending_eof = 2;
2557 return;
2558 } else if (ch == SCHEME_SPECIAL) {
2559 ip->ungotten_special = ip->special;
2560 ip->special = NULL;
2561 } else if (ch > 127) {
2562 unsigned char e[MAX_UTF8_CHAR_BYTES];
2563 unsigned int us[1];
2564 int len;
2565
2566 us[0] = ch;
2567 len = scheme_utf8_encode_all(us, 1, e);
2568
2569 if (ip->p.position > (len - 1))
2570 ip->p.position -= (len - 1);
2571
2572 if (ip->ungotten_count + len >= 24)
2573 scheme_signal_error("ungetc overflow");
2574 while (len) {
2575 ip->ungotten[ip->ungotten_count++] = e[--len];
2576 }
2577 } else {
2578 if (ip->ungotten_count == 24)
2579 scheme_signal_error("ungetc overflow");
2580 ip->ungotten[ip->ungotten_count++] = ch;
2581 }
2582
2583 if (ip->p.position > 0)
2584 --ip->p.position;
2585 if (ip->p.count_lines) {
2586 --ip->p.column;
2587 --ip->p.readpos;
2588 if (!(--ip->p.charsSinceNewline)) {
2589 mzAssert(ip->p.lineNumber > 0);
2590 --ip->p.lineNumber;
2591 ip->p.column = ip->p.oldColumn;
2592 } else if (ch == '\t')
2593 ip->p.column = ip->p.oldColumn;
2594 }
2595 }
2596
byte_input_ready(Scheme_Object * port,Scheme_Schedule_Info * sinfo)2597 int byte_input_ready (Scheme_Object *port, Scheme_Schedule_Info *sinfo)
2598 {
2599 Scheme_Input_Port *ip;
2600 int retval;
2601
2602 ip = scheme_input_port_record(port);
2603
2604 CHECK_PORT_CLOSED("char-ready?", "input", port, ip->closed);
2605
2606 if (ip->slow
2607 && (ip->ungotten_count || ip->ungotten_special
2608 || (ip->pending_eof > 1)
2609 || pipe_char_count(ip->peeked_read)))
2610 retval = 1;
2611 else {
2612 Scheme_In_Ready_Fun_FPC f;
2613 f = (Scheme_In_Ready_Fun_FPC)ip->byte_ready_fun;
2614 retval = f(ip, NULL);
2615 }
2616
2617 return retval;
2618 }
2619
2620 int
scheme_byte_ready(Scheme_Object * port)2621 scheme_byte_ready (Scheme_Object *port)
2622 {
2623 return byte_input_ready(port, NULL);
2624 }
2625
2626 int
scheme_char_ready(Scheme_Object * port)2627 scheme_char_ready (Scheme_Object *port)
2628 {
2629 int unavail;
2630
2631 if (!scheme_byte_ready(port))
2632 return 0;
2633
2634 do_peekc_skip(port, scheme_make_integer(0), 2, &unavail);
2635
2636 return !unavail;
2637 }
2638
scheme_get_special(Scheme_Object * port,Scheme_Object * src,intptr_t line,intptr_t col,intptr_t pos,int peek,Scheme_Hash_Table ** for_read)2639 Scheme_Object *scheme_get_special(Scheme_Object *port,
2640 Scheme_Object *src, intptr_t line, intptr_t col, intptr_t pos,
2641 int peek, Scheme_Hash_Table **for_read)
2642 {
2643 int cnt;
2644 Scheme_Object *a[4], *special;
2645 Scheme_Input_Port *ip;
2646
2647 SCHEME_USE_FUEL(1);
2648
2649 ip = scheme_input_port_record(port);
2650
2651 /* Only `read' and similar internals should call this function. A
2652 caller must should ensure that there are no ungotten
2653 characters. */
2654
2655 if (ip->ungotten_count) {
2656 scheme_signal_error("ungotten characters at get-special");
2657 return NULL;
2658 }
2659 if (!ip->special) {
2660 scheme_signal_error("no ready special");
2661 return NULL;
2662 }
2663
2664 CHECK_PORT_CLOSED("#<primitive:get-special>", "input", port, ip->closed);
2665
2666 special = ip->special;
2667 ip->special = NULL;
2668
2669 if (peek) {
2670 /* do location increment, since read didn't */
2671 if (col >= 0)
2672 col++;
2673 if (pos > 0)
2674 pos++;
2675 }
2676
2677 a[0] = special;
2678 if (!src && scheme_check_proc_arity(NULL, 2, 0, 1, a))
2679 cnt = 0;
2680 else {
2681 cnt = 4;
2682 a[0] = (src ? src : scheme_false);
2683 a[1] = (line > 0) ? scheme_make_integer(line) : scheme_false;
2684 a[2] = (col > 0) ? scheme_make_integer(col-1) : scheme_false;
2685 a[3] = (pos > 0) ? scheme_make_integer(pos) : scheme_false;
2686 }
2687
2688 special = scheme_apply(special, cnt, a);
2689
2690 return special;
2691 }
2692
do_get_ready_special(Scheme_Object * port,Scheme_Object * stxsrc,int peek,Scheme_Hash_Table ** ht)2693 static Scheme_Object *do_get_ready_special(Scheme_Object *port,
2694 Scheme_Object *stxsrc,
2695 int peek,
2696 Scheme_Hash_Table **ht)
2697 {
2698 intptr_t line, col, pos;
2699
2700 if (!stxsrc) {
2701 Scheme_Input_Port *ip;
2702 ip = scheme_input_port_record(port);
2703 stxsrc = ip->name;
2704 }
2705
2706 scheme_tell_all(port, &line, &col, &pos);
2707
2708 return scheme_get_special(port, stxsrc, line, col, pos, peek, ht);
2709 }
2710
scheme_get_ready_read_special(Scheme_Object * port,Scheme_Object * stxsrc,Scheme_Hash_Table ** ht)2711 Scheme_Object *scheme_get_ready_read_special(Scheme_Object *port, Scheme_Object *stxsrc, Scheme_Hash_Table **ht)
2712 {
2713 return do_get_ready_special(port, stxsrc, 0, ht);
2714 }
2715
scheme_get_ready_special(Scheme_Object * port,Scheme_Object * stxsrc,int peek)2716 Scheme_Object *scheme_get_ready_special(Scheme_Object *port,
2717 Scheme_Object *stxsrc,
2718 int peek)
2719 {
2720 return do_get_ready_special(port, stxsrc, peek, NULL);
2721 }
2722
scheme_bad_time_for_special(const char * who,Scheme_Object * port)2723 void scheme_bad_time_for_special(const char *who, Scheme_Object *port)
2724 {
2725 scheme_contract_error(who, "non-character in an unsupported context",
2726 "port", 1, port,
2727 NULL);
2728 }
2729
check_special_args(void * sbox,int argc,Scheme_Object ** argv)2730 static Scheme_Object *check_special_args(void *sbox, int argc, Scheme_Object **argv)
2731 {
2732 Scheme_Object *special;
2733
2734 if (SCHEME_TRUEP(argv[1]))
2735 if (!scheme_nonneg_exact_p(argv[1]) || (SAME_OBJ(argv[1], scheme_make_integer(0))))
2736 scheme_wrong_contract("read-special", "(or/c exact-positive-integer? #f)", 1, argc, argv);
2737 if (SCHEME_TRUEP(argv[2]))
2738 if (!scheme_nonneg_exact_p(argv[2]))
2739 scheme_wrong_contract("read-special", "(or/c exact-nonnegative-integer? #f)", 2, argc, argv);
2740 if (SCHEME_TRUEP(argv[3]))
2741 if (!scheme_nonneg_exact_p(argv[3]) || (SAME_OBJ(argv[3], scheme_make_integer(0))))
2742 scheme_wrong_contract("read-special", "(or/c exact-positive-integer? #f)", 3, argc, argv);
2743
2744 special = *(Scheme_Object **)sbox;
2745 if (!special)
2746 scheme_raise_exn(MZEXN_FAIL_CONTRACT,
2747 "read-special: cannot be called a second time");
2748 *(Scheme_Object **)sbox = NULL;
2749
2750 special = _scheme_apply(special, 4, argv);
2751
2752 return special;
2753 }
2754
scheme_get_special_proc(Scheme_Object * inport)2755 Scheme_Object *scheme_get_special_proc(Scheme_Object *inport)
2756 {
2757 Scheme_Object *special, **sbox;
2758 Scheme_Input_Port *ip;
2759
2760 ip = scheme_input_port_record(inport);
2761 special = ip->special;
2762 ip->special = NULL;
2763
2764 sbox = MALLOC_ONE(Scheme_Object *);
2765 *sbox = special;
2766 return scheme_make_closed_prim_w_arity(check_special_args,
2767 sbox, "read-special",
2768 4, 4);
2769 }
2770
2771 void
scheme_need_wakeup(Scheme_Object * port,void * fds)2772 scheme_need_wakeup (Scheme_Object *port, void *fds)
2773 {
2774 Scheme_Input_Port *ip;
2775
2776 ip = scheme_input_port_record(port);
2777
2778 if (ip->need_wakeup_fun) {
2779 Scheme_Need_Wakeup_Input_Fun f = ip->need_wakeup_fun;
2780 f(ip, fds);
2781 }
2782 }
2783
2784 #define CHECK_IOPORT_CLOSED(who, port) \
2785 if (SCHEME_INPORTP((Scheme_Object *)port)) { \
2786 CHECK_PORT_CLOSED(who, "input", port, ((Scheme_Input_Port *)port)->closed); \
2787 } else { \
2788 CHECK_PORT_CLOSED(who, "output", port, ((Scheme_Output_Port *)port)->closed); \
2789 }
2790
check_input_port_lock(Scheme_Port * ip)2791 static void check_input_port_lock(Scheme_Port *ip)
2792 {
2793 if (SCHEME_INPORTP(ip)) {
2794 Scheme_Input_Port *iip = (Scheme_Input_Port *)ip;
2795 if (iip->input_lock)
2796 scheme_wait_input_allowed(iip, 0);
2797 }
2798 }
2799
2800 static intptr_t
do_tell(Scheme_Object * port,int not_via_loc)2801 do_tell (Scheme_Object *port, int not_via_loc)
2802 {
2803 Scheme_Port *ip;
2804 intptr_t pos;
2805
2806 ip = scheme_port_record(port);
2807
2808 check_input_port_lock(ip);
2809
2810 CHECK_IOPORT_CLOSED("get-file-position", ip);
2811
2812 if (not_via_loc || !ip->count_lines || (ip->position < 0))
2813 pos = ip->position;
2814 else
2815 pos = ip->readpos;
2816
2817 return pos;
2818 }
2819
2820 intptr_t
scheme_tell(Scheme_Object * port)2821 scheme_tell (Scheme_Object *port)
2822 {
2823 return do_tell(port, 0);
2824 }
2825
2826 intptr_t
scheme_tell_line(Scheme_Object * port)2827 scheme_tell_line (Scheme_Object *port)
2828 {
2829 Scheme_Port *ip;
2830 intptr_t line;
2831
2832 ip = scheme_port_record(port);
2833
2834 if (!ip->count_lines || (ip->position < 0))
2835 return -1;
2836
2837 check_input_port_lock(ip);
2838
2839 CHECK_IOPORT_CLOSED("get-file-line", ip);
2840
2841 line = ip->lineNumber;
2842
2843 return line;
2844 }
2845
2846 intptr_t
scheme_tell_column(Scheme_Object * port)2847 scheme_tell_column (Scheme_Object *port)
2848 {
2849 Scheme_Port *ip;
2850 intptr_t col;
2851
2852 ip = scheme_port_record(port);
2853
2854 if (!ip->count_lines || (ip->position < 0))
2855 return -1;
2856
2857 check_input_port_lock(ip);
2858
2859 CHECK_IOPORT_CLOSED("get-file-column", ip);
2860
2861 col = ip->column;
2862
2863 return col;
2864 }
2865
2866
extract_next_location(const char * who,int argc,Scheme_Object ** a,int delta,intptr_t * _line,intptr_t * _col,intptr_t * _pos)2867 static void extract_next_location(const char *who, int argc, Scheme_Object **a, int delta,
2868 intptr_t *_line, intptr_t *_col, intptr_t *_pos)
2869 {
2870 int i, j;
2871 intptr_t v;
2872 intptr_t line = -1, col = -1, pos = -1;
2873
2874 for (j = 0; j < 3; j++) {
2875 v = -1;
2876 i = j + delta;
2877 if (SCHEME_TRUEP(a[i])) {
2878 if (scheme_nonneg_exact_p(a[i])) {
2879 if (SCHEME_INTP(a[i])) {
2880 v = SCHEME_INT_VAL(a[i]);
2881 if ((j != 1) && !v) {
2882 v = -1;
2883 }
2884 }
2885 }
2886 if (v == -1) {
2887 if (argc < 0)
2888 a[0] = a[i];
2889 scheme_wrong_contract(who,
2890 ((j == 1) ? "(or/c exact-nonnegative-integer? #f)" : "(or/c exact-positive-integer? #f)"),
2891 ((argc > 0) ? i : -1), argc, a);
2892 return;
2893 }
2894 }
2895
2896 switch (j) {
2897 case 0:
2898 line = v;
2899 break;
2900 case 1:
2901 col = v;
2902 break;
2903 case 2:
2904 pos = v;
2905 break;
2906 }
2907 }
2908
2909 /* Internally, positions count from 0 instead of 1 */
2910 if (pos > -1)
2911 pos--;
2912
2913 if (_line) *_line = line;
2914 if (_col) *_col = col;
2915 if (_pos) *_pos = pos;
2916 }
2917
2918 void
scheme_tell_all(Scheme_Object * port,intptr_t * _line,intptr_t * _col,intptr_t * _pos)2919 scheme_tell_all (Scheme_Object *port, intptr_t *_line, intptr_t *_col, intptr_t *_pos)
2920 {
2921 Scheme_Port *ip;
2922
2923 ip = scheme_port_record(port);
2924
2925 if (ip->count_lines && ip->location_fun) {
2926 Scheme_Location_Fun location_fun;
2927 Scheme_Object *r, *a[3];
2928 int got;
2929
2930 location_fun = ip->location_fun;
2931 r = location_fun(ip);
2932
2933 got = (SAME_OBJ(r, SCHEME_MULTIPLE_VALUES) ? scheme_multiple_count : 1);
2934 if (got != 3) {
2935 scheme_wrong_return_arity("user port next-location",
2936 3, got,
2937 (got == 1) ? (Scheme_Object **)r : scheme_multiple_array,
2938 "calling port-next-location procedure");
2939 return;
2940 }
2941
2942 a[0] = scheme_multiple_array[0];
2943 a[1] = scheme_multiple_array[1];
2944 a[2] = scheme_multiple_array[2];
2945
2946 extract_next_location("user port next-location", -1, a, 0, _line, _col, _pos);
2947 } else {
2948 intptr_t line, col, pos;
2949
2950 line = scheme_tell_line(port);
2951 col = scheme_tell_column(port);
2952 pos = scheme_tell_can_redirect(port, 0);
2953
2954 if (_line) *_line = line;
2955 if (_col) *_col = col;
2956 if (_pos) *_pos = pos;
2957 }
2958 }
2959
2960 intptr_t
scheme_tell_can_redirect(Scheme_Object * port,int not_via_loc)2961 scheme_tell_can_redirect (Scheme_Object *port, int not_via_loc)
2962 {
2963 Scheme_Port *ip;
2964 Scheme_Object *v;
2965
2966 while (1) {
2967 ip = scheme_port_record(port);
2968
2969 if (ip->position_redirect) {
2970 if (SCHEME_INPUT_PORTP(ip->position_redirect)
2971 || SCHEME_OUTPUT_PORTP(ip->position_redirect)) {
2972 SCHEME_USE_FUEL(1);
2973 port = ip->position_redirect;
2974 } else {
2975 v = scheme_apply(ip->position_redirect, 0, NULL);
2976 if (SCHEME_INTP(v) && (SCHEME_INT_VAL(v) >= 1))
2977 return SCHEME_INT_VAL(v) - 1;
2978 else if (SCHEME_FALSEP(v) || (SCHEME_BIGNUMP(v) && SCHEME_BIGPOS(v)))
2979 return -1;
2980 else {
2981 Scheme_Object *a[1];
2982 a[0] = v;
2983 scheme_wrong_contract("file-position", "exact-positive-integer?", 0, -1, a);
2984 return -1;
2985 }
2986 }
2987 } else
2988 break;
2989 }
2990
2991 return do_tell(port, not_via_loc);
2992 }
2993
scheme_set_port_location(int argc,Scheme_Object ** argv)2994 void scheme_set_port_location(int argc, Scheme_Object **argv)
2995 {
2996 Scheme_Port *ip;
2997 intptr_t line, col, pos;
2998
2999 extract_next_location("set-port-next-location!", argc, argv,
3000 1, &line, &col, &pos);
3001
3002
3003 ip = scheme_port_record(argv[0]);
3004
3005 if (ip->count_lines) {
3006 ip->readpos = pos;
3007 ip->lineNumber = line;
3008 ip->column = col;
3009 }
3010 }
3011
3012 void
scheme_count_lines(Scheme_Object * port)3013 scheme_count_lines (Scheme_Object *port)
3014 {
3015 Scheme_Port *ip;
3016
3017 ip = scheme_port_record(port);
3018
3019 if (!ip->count_lines) {
3020 ip->count_lines = 1;
3021 if (ip->count_lines_fun) {
3022 Scheme_Count_Lines_Fun cl = ip->count_lines_fun;
3023 cl(ip);
3024 }
3025
3026 if (scheme_is_input_port(port)) {
3027 Scheme_Input_Port *iip;
3028 iip = scheme_input_port_record(port);
3029 if (iip)
3030 iip->slow = 1;
3031 }
3032 }
3033 }
3034
3035 void
scheme_close_input_port(Scheme_Object * port)3036 scheme_close_input_port (Scheme_Object *port)
3037 {
3038 Scheme_Input_Port *ip;
3039
3040 ip = scheme_input_port_record(port);
3041
3042 if (ip->input_lock && scheme_force_port_closed)
3043 scheme_wait_input_allowed(ip, 0);
3044
3045 if (!ip->closed) {
3046 if (ip->close_fun) {
3047 Scheme_Close_Input_Fun f = ip->close_fun;
3048 f(ip);
3049 }
3050
3051 if (ip->progress_evt) {
3052 scheme_post_sema_all(ip->progress_evt);
3053 ip->progress_evt = NULL;
3054 }
3055
3056 if (ip->mref) {
3057 scheme_remove_managed(ip->mref, (Scheme_Object *)ip);
3058 ip->mref = NULL;
3059 }
3060
3061 ip->closed = 1;
3062 ip->slow = 1;
3063 ip->ungotten_count = 0;
3064 ip->ungotten_special = NULL;
3065 if (ip->closed_evt)
3066 scheme_post_sema_all(SCHEME_PTR_VAL(ip->closed_evt));
3067 }
3068 }
3069
3070 static void
force_close_input_port(Scheme_Object * port)3071 force_close_input_port(Scheme_Object *port)
3072 {
3073 scheme_force_port_closed = 1;
3074 scheme_close_input_port(port);
3075 scheme_force_port_closed = 0;
3076 }
3077
scheme_close_should_force_port_closed()3078 int scheme_close_should_force_port_closed()
3079 {
3080 return scheme_force_port_closed;
3081 }
3082
3083 /****************************** main output writer ******************************/
3084
3085 static intptr_t
put_byte_string_slow(const char * who,Scheme_Object * port,const char * str,intptr_t d,intptr_t len,int rarely_block)3086 put_byte_string_slow(const char *who, Scheme_Object *port,
3087 const char *str, intptr_t d, intptr_t len,
3088 int rarely_block)
3089 {
3090 /* Unlike the main reader, the main writer is simple. It doesn't
3091 have to deal with peeks and specials, so it's a thin wrapper on
3092 the port's function. */
3093
3094 Scheme_Output_Port *op;
3095 Scheme_Write_String_Fun ws;
3096 intptr_t out, llen, oout;
3097 int enable_break;
3098
3099 op = scheme_output_port_record(port);
3100
3101 CHECK_PORT_CLOSED(who, "output", port, op->closed);
3102
3103 ws = op->write_string_fun;
3104
3105 if (rarely_block == -1) {
3106 enable_break = 1;
3107 rarely_block = 1;
3108 } else
3109 enable_break = 0;
3110
3111 if (enable_break) {
3112 if (scheme_current_thread->external_break) {
3113 scheme_thread_block_enable_break(0.0, 1);
3114 scheme_current_thread->ran_some = 1;
3115 }
3116 }
3117
3118 if ((rarely_block == 1) && !len)
3119 /* By definition, a partial-progress write on a 0-length string is
3120 the same as a blocking flush */
3121 rarely_block = 0;
3122
3123 llen = len;
3124 oout = 0;
3125 while (llen || !len) {
3126 out = ws(op, str, d, llen, rarely_block, enable_break);
3127
3128 /* If out is 0, it might be because the port got closed: */
3129 if (!out) {
3130 CHECK_PORT_CLOSED(who, "output", port, op->closed);
3131 }
3132
3133 if (out > 0) {
3134 op->p.position += out;
3135 oout += out;
3136 if (op->p.count_lines)
3137 do_count_lines((Scheme_Port *)op, str, d, out);
3138 }
3139
3140 if (rarely_block || !len)
3141 break;
3142
3143 llen -= out;
3144 d += out;
3145 }
3146
3147 mzAssert(!rarely_block ? (oout == len) : 1);
3148 mzAssert((oout < 0) ? (rarely_block == 2) : 1);
3149
3150 return oout;
3151 }
3152
3153 intptr_t
scheme_put_byte_string(GC_CAN_IGNORE const char * who,Scheme_Object * port,GC_CAN_IGNORE const char * str,intptr_t d,intptr_t len,int rarely_block)3154 scheme_put_byte_string(GC_CAN_IGNORE const char *who, Scheme_Object *port,
3155 GC_CAN_IGNORE const char *str, intptr_t d, intptr_t len,
3156 int rarely_block)
3157 {
3158 intptr_t out;
3159
3160 if (SCHEME_OUTPORTP(port)
3161 && !((Scheme_Output_Port *)port)->closed
3162 && (rarely_block != -1)
3163 && (len == 1)
3164 && !((Scheme_Output_Port *)port)->p.count_lines) {
3165 Scheme_Output_Port *op = (Scheme_Output_Port *)port;
3166 Scheme_Write_String_Fun ws;
3167 ws = op->write_string_fun;
3168 out = ws(op, str, d, 1, rarely_block, 0);
3169 if (out) {
3170 op->p.position += out;
3171 return out;
3172 } else if (rarely_block)
3173 return 0;
3174 }
3175
3176 return put_byte_string_slow(who, port, str, d, len, rarely_block);
3177 }
3178
scheme_write_byte_string(const char * str,intptr_t len,Scheme_Object * port)3179 void scheme_write_byte_string(const char *str, intptr_t len, Scheme_Object *port)
3180 {
3181 (void)scheme_put_byte_string("write-string", port, str, 0, len, 0);
3182 }
3183
scheme_write_char_string(const mzchar * str,intptr_t len,Scheme_Object * port)3184 void scheme_write_char_string(const mzchar *str, intptr_t len, Scheme_Object *port)
3185 {
3186 intptr_t blen;
3187 char *bstr, buf[64];
3188
3189 bstr = scheme_utf8_encode_to_buffer_len(str, len, buf, 64, &blen);
3190
3191 scheme_write_byte_string(bstr, blen, port);
3192 }
3193
3194 intptr_t
scheme_put_char_string(const char * who,Scheme_Object * port,const mzchar * str,intptr_t d,intptr_t len)3195 scheme_put_char_string(const char *who, Scheme_Object *port,
3196 const mzchar *str, intptr_t d, intptr_t len)
3197 {
3198 intptr_t blen;
3199 char *bstr, buf[64];
3200
3201 blen = scheme_utf8_encode(str, d, d + len, NULL, 0, 0);
3202 if (blen < 64)
3203 bstr = buf;
3204 else
3205 bstr = (char *)scheme_malloc_atomic(blen);
3206 scheme_utf8_encode(str, d, d + len, (unsigned char *)bstr, 0, 0);
3207
3208 return scheme_put_byte_string(who, port, bstr, 0, blen, 0);
3209 }
3210
3211 intptr_t
scheme_output_tell(Scheme_Object * port)3212 scheme_output_tell(Scheme_Object *port)
3213 {
3214 return scheme_tell(port);
3215 }
3216
3217 void
scheme_close_output_port(Scheme_Object * port)3218 scheme_close_output_port(Scheme_Object *port)
3219 {
3220 Scheme_Output_Port *op;
3221
3222 op = scheme_output_port_record(port);
3223
3224 if (!op->closed) {
3225 /* call close function first; it might raise an exception */
3226 if (op->close_fun) {
3227 Scheme_Close_Output_Fun f = op->close_fun;
3228 f(op);
3229 }
3230
3231 /* NOTE: Allow the possibility that some other thread finishes the
3232 close while f blocks. */
3233
3234 if (op->mref) {
3235 scheme_remove_managed(op->mref, (Scheme_Object *)op);
3236 op->mref = NULL;
3237 }
3238
3239 op->closed = 1;
3240 if (op->closed_evt)
3241 scheme_post_sema_all(SCHEME_PTR_VAL(op->closed_evt));
3242 }
3243 }
3244
3245 static void
force_close_output_port(Scheme_Object * port)3246 force_close_output_port(Scheme_Object *port)
3247 {
3248 scheme_force_port_closed = 1;
3249 scheme_close_output_port(port);
3250 scheme_force_port_closed = 0;
3251 }
3252
3253 /*========================================================================*/
3254 /* File port utils */
3255 /*========================================================================*/
3256
scheme_flush_orig_outputs(void)3257 void scheme_flush_orig_outputs(void)
3258 {
3259 /* Flush original output ports: */
3260 if (flush_out)
3261 scheme_flush_output(scheme_orig_stdout_port);
3262 if (flush_err)
3263 scheme_flush_output(scheme_orig_stderr_port);
3264 }
3265
scheme_flush_output(Scheme_Object * o)3266 void scheme_flush_output(Scheme_Object *o)
3267 {
3268 scheme_put_byte_string("flush-output", o,
3269 NULL, 0, 0,
3270 0);
3271 }
3272
3273 Scheme_Object *
scheme_file_stream_port_p(int argc,Scheme_Object * argv[])3274 scheme_file_stream_port_p (int argc, Scheme_Object *argv[])
3275 {
3276 Scheme_Object *p = argv[0];
3277
3278 if (SCHEME_INPUT_PORTP(p)) {
3279 Scheme_Input_Port *ip;
3280
3281 ip = scheme_input_port_record(p);
3282
3283 if (SAME_OBJ(ip->sub_type, file_input_port_type))
3284 return scheme_true;
3285 else if (SAME_OBJ(ip->sub_type, fd_input_port_type))
3286 return scheme_true;
3287 } else if (SCHEME_OUTPUT_PORTP(p)) {
3288 Scheme_Output_Port *op;
3289
3290 op = scheme_output_port_record(p);
3291
3292 if (SAME_OBJ(op->sub_type, file_output_port_type))
3293 return scheme_true;
3294 else if (SAME_OBJ(op->sub_type, fd_output_port_type))
3295 return scheme_true;
3296 }
3297
3298 return scheme_false;
3299 }
3300
scheme_port_waiting_peer_p(int argc,Scheme_Object * argv[])3301 Scheme_Object *scheme_port_waiting_peer_p(int argc, Scheme_Object *argv[])
3302 {
3303 Scheme_Object *p = argv[0];
3304
3305 if (SCHEME_OUTPUT_PORTP(p)) {
3306 Scheme_Output_Port *op;
3307
3308 op = scheme_output_port_record(p);
3309
3310 if (SAME_OBJ(op->sub_type, fd_output_port_type)) {
3311 rktio_fd_t *rfd = ((Scheme_FD *)op->port_data)->fd;
3312 if (rktio_fd_is_pending_open(scheme_rktio, rfd))
3313 return scheme_true;
3314 }
3315 } else if (SCHEME_INPUT_PORTP(p)) {
3316 /* ok */
3317 } else {
3318 scheme_wrong_contract("port-waiting-peer?", "port?", 0, argc, argv);
3319 ESCAPED_BEFORE_HERE;
3320 }
3321
3322 return scheme_false;
3323 }
3324
scheme_get_port_file_descriptor(Scheme_Object * p,intptr_t * _fd)3325 int scheme_get_port_file_descriptor(Scheme_Object *p, intptr_t *_fd)
3326 {
3327 intptr_t fd = 0;
3328 int fd_ok = 0;
3329
3330 if (SCHEME_INPUT_PORTP(p)) {
3331 Scheme_Input_Port *ip;
3332
3333 ip = scheme_input_port_record(p);
3334
3335 if (!ip->closed) {
3336 if (SAME_OBJ(ip->sub_type, file_input_port_type)) {
3337 fd = MSC_IZE(fileno)((FILE *)((Scheme_Input_File *)ip->port_data)->f);
3338 fd_ok = 1;
3339 } else if (SAME_OBJ(ip->sub_type, fd_input_port_type)) {
3340 fd = rktio_fd_system_fd(scheme_rktio, ((Scheme_FD *)ip->port_data)->fd);
3341 fd_ok = 1;
3342 }
3343 }
3344 } else if (SCHEME_OUTPUT_PORTP(p)) {
3345 Scheme_Output_Port *op;
3346
3347 op = scheme_output_port_record(p);
3348
3349 if (!op->closed) {
3350 if (SAME_OBJ(op->sub_type, file_output_port_type)) {
3351 fd = MSC_IZE (fileno)((FILE *)((Scheme_Output_File *)op->port_data)->f);
3352 fd_ok = 1;
3353 } else if (SAME_OBJ(op->sub_type, fd_output_port_type)) {
3354 rktio_fd_t *rfd = ((Scheme_FD *)op->port_data)->fd;
3355 if (!rktio_fd_is_pending_open(scheme_rktio, rfd)) {
3356 fd = rktio_fd_system_fd(scheme_rktio, rfd);
3357 fd_ok = 1;
3358 }
3359 }
3360 }
3361 }
3362
3363 if (!fd_ok)
3364 return 0;
3365
3366 *_fd = fd;
3367 return 1;
3368 }
3369
scheme_get_port_rktio_file_descriptor(Scheme_Object * p,rktio_fd_t ** _fd)3370 int scheme_get_port_rktio_file_descriptor(Scheme_Object *p, rktio_fd_t **_fd)
3371 {
3372 if (SCHEME_INPUT_PORTP(p)) {
3373 Scheme_Input_Port *ip;
3374
3375 ip = scheme_input_port_record(p);
3376
3377 if (!ip->closed) {
3378 if (SAME_OBJ(ip->sub_type, fd_input_port_type)) {
3379 *_fd = ((Scheme_FD *)ip->port_data)->fd;
3380 return 1;
3381 }
3382 }
3383 } else if (SCHEME_OUTPUT_PORTP(p)) {
3384 Scheme_Output_Port *op;
3385
3386 op = scheme_output_port_record(p);
3387
3388 if (!op->closed) {
3389 if (SAME_OBJ(op->sub_type, fd_output_port_type)) {
3390 *_fd = ((Scheme_FD *)op->port_data)->fd;
3391 return 1;
3392 }
3393 }
3394 }
3395
3396 return 0;
3397 }
3398
scheme_get_port_fd(Scheme_Object * p)3399 intptr_t scheme_get_port_fd(Scheme_Object *p)
3400 {
3401 intptr_t fd;
3402
3403 if (scheme_get_port_file_descriptor(p, &fd))
3404 return fd;
3405 else
3406 return -1;
3407 }
3408
unsafe_handle_to_port(const char * who,int argc,Scheme_Object * argv[],int socket)3409 static Scheme_Object *unsafe_handle_to_port(const char *who, int argc, Scheme_Object *argv[], int socket)
3410 {
3411 Scheme_Object *name = argv[1], *l, *a;
3412 intptr_t s;
3413 int closemode = 1;
3414 int regfile = 0;
3415 int textmode = 0;
3416 int readmode = 0, writemode = 0;
3417
3418 if (!scheme_get_int_val(argv[0], &s))
3419 scheme_wrong_contract(who, "handle-integer?", 0, argc, argv);
3420
3421 if (socket) {
3422 if (!SCHEME_BYTE_STRINGP(name))
3423 scheme_wrong_contract(who, "bytes?", 1, argc, argv);
3424 }
3425
3426 l = argv[2];
3427 while (SCHEME_PAIRP(l)) {
3428 a = SCHEME_CAR(l);
3429 if (!SCHEME_SYMBOLP(a) || SCHEME_SYM_WEIRDP(a))
3430 break;
3431 if (socket) {
3432 if (!strcmp(SCHEME_SYM_VAL(a), "no-close"))
3433 closemode = 0;
3434 } else {
3435 if (!strcmp(SCHEME_SYM_VAL(a), "read"))
3436 readmode = 1;
3437 else if (!strcmp(SCHEME_SYM_VAL(a), "write"))
3438 writemode = 1;
3439 else if (!strcmp(SCHEME_SYM_VAL(a), "text"))
3440 textmode = 1;
3441 else if (!strcmp(SCHEME_SYM_VAL(a), "regular-file"))
3442 regfile = 1;
3443 else
3444 break;
3445 }
3446 l = SCHEME_CDR(l);
3447 }
3448 if (!SCHEME_NULLP(l))
3449 scheme_wrong_contract(who, "mode-symbol-list?", 2, argc, argv);
3450
3451 if (socket) {
3452 Scheme_Object *p[2];
3453 scheme_socket_to_ports(s, SCHEME_BYTE_STR_VAL(name), closemode, &p[0], &p[1]);
3454 return scheme_values(2, p);
3455 } else if (writemode)
3456 return scheme_make_fd_output_port(s, name, regfile, textmode, readmode);
3457 else if (readmode)
3458 return scheme_make_fd_input_port(s, name, regfile, textmode);
3459 else {
3460 scheme_contract_error(who,
3461 "mode list must include at least one of 'read or 'write"
3462 "mode list", 1, argv[2],
3463 NULL);
3464 return NULL;
3465 }
3466 }
3467
unsafe_fd_to_port(int argc,Scheme_Object * argv[])3468 static Scheme_Object *unsafe_fd_to_port(int argc, Scheme_Object *argv[])
3469 {
3470 return unsafe_handle_to_port("unsafe-file-descriptor->port", argc, argv, 0);
3471 }
3472
unsafe_socket_to_port(int argc,Scheme_Object * argv[])3473 static Scheme_Object *unsafe_socket_to_port(int argc, Scheme_Object *argv[])
3474 {
3475 return unsafe_handle_to_port("unsafe-socket->port", argc, argv, 1);
3476 }
3477
unsafe_port_to_fd(int argc,Scheme_Object * argv[])3478 static Scheme_Object *unsafe_port_to_fd(int argc, Scheme_Object *argv[])
3479 {
3480 intptr_t s;
3481
3482 if (scheme_get_port_file_descriptor(argv[0], &s))
3483 return scheme_make_integer_value(s);
3484 else {
3485 if (!SCHEME_INPUT_PORTP(argv[0]) && !SCHEME_OUTPUT_PORTP(argv[0]))
3486 scheme_wrong_contract("unsafe-port->file-descriptor", "port?", 0, argc, argv);
3487 return scheme_false;
3488 }
3489 }
3490
unsafe_port_to_socket(int argc,Scheme_Object * argv[])3491 static Scheme_Object *unsafe_port_to_socket(int argc, Scheme_Object *argv[])
3492 {
3493 intptr_t s;
3494
3495 if (scheme_get_port_socket(argv[0], &s))
3496 return scheme_make_integer_value(s);
3497 else {
3498 if (!SCHEME_INPUT_PORTP(argv[0]) && !SCHEME_OUTPUT_PORTP(argv[0]))
3499 scheme_wrong_contract("unsafe-port->socket", "port?", 0, argc, argv);
3500 return scheme_false;
3501 }
3502 }
3503
unsafe_handle_to_semaphore(const char * who,int argc,Scheme_Object * argv[],int is_socket)3504 static Scheme_Object *unsafe_handle_to_semaphore(const char *who, int argc, Scheme_Object *argv[], int is_socket)
3505 {
3506 Scheme_Object *a = argv[1];
3507 intptr_t s;
3508 int mode;
3509
3510 if (!scheme_get_int_val(argv[0], &s))
3511 scheme_wrong_contract(who, "handle-integer?", 0, argc, argv);
3512
3513 if (!SCHEME_SYMBOLP(a) || SCHEME_SYM_WEIRDP(a))
3514 mode = -1;
3515 else if (!strcmp(SCHEME_SYM_VAL(a), "read"))
3516 mode = MZFD_CREATE_READ;
3517 else if (!strcmp(SCHEME_SYM_VAL(a), "write"))
3518 mode = MZFD_CREATE_WRITE;
3519 else if (!strcmp(SCHEME_SYM_VAL(a), "check-read"))
3520 mode = MZFD_CHECK_READ;
3521 else if (!strcmp(SCHEME_SYM_VAL(a), "check-write"))
3522 mode = MZFD_CHECK_WRITE;
3523 else if (!strcmp(SCHEME_SYM_VAL(a), "remove"))
3524 mode = MZFD_REMOVE;
3525 else
3526 mode = -1;
3527
3528 if (mode == -1)
3529 scheme_wrong_contract(who, "semaphore-mode-symbol?", 1, argc, argv);
3530
3531 a = scheme_fd_to_semaphore(s, mode, is_socket);
3532 return (a ? a : scheme_false);
3533 }
3534
unsafe_fd_to_semaphore(int argc,Scheme_Object * argv[])3535 static Scheme_Object *unsafe_fd_to_semaphore(int argc, Scheme_Object *argv[])
3536 {
3537 return unsafe_handle_to_semaphore("unsafe-file-descriptor->semaphore", argc, argv, 0);
3538 }
3539
unsafe_socket_to_semaphore(int argc,Scheme_Object * argv[])3540 static Scheme_Object *unsafe_socket_to_semaphore(int argc, Scheme_Object *argv[])
3541 {
3542 return unsafe_handle_to_semaphore("unsafe-socket->semaphore", argc, argv, 1);
3543 }
3544
scheme_file_identity(int argc,Scheme_Object * argv[])3545 Scheme_Object *scheme_file_identity(int argc, Scheme_Object *argv[])
3546 {
3547 intptr_t fd = 0;
3548 int fd_ok = 0;
3549 Scheme_Object *p;
3550
3551 p = argv[0];
3552
3553 fd_ok = scheme_get_port_file_descriptor(p, &fd);
3554
3555 if (!fd_ok) {
3556 /* Maybe failed because it was closed... */
3557 if (SCHEME_INPUT_PORTP(p)) {
3558 Scheme_Input_Port *ip;
3559
3560 ip = scheme_input_port_record(p);
3561
3562 CHECK_PORT_CLOSED("port-file-identity", "input", p, ip->closed);
3563 } else if (SCHEME_OUTPUT_PORTP(p)) {
3564 Scheme_Output_Port *op;
3565
3566 op = scheme_output_port_record(p);
3567
3568 CHECK_PORT_CLOSED("port-file-identity", "output", p, op->closed);
3569 }
3570
3571 /* Otherwise, it's just the wrong type: */
3572 scheme_wrong_contract("port-file-identity", "file-stream-port?", 0, argc, argv);
3573 return NULL;
3574 }
3575
3576 return scheme_get_fd_identity(p, fd, NULL, 0);
3577 }
3578
is_fd_terminal(intptr_t fd)3579 static int is_fd_terminal(intptr_t fd)
3580 {
3581 rktio_fd_t *rfd;
3582 int is_term;
3583
3584 rfd = rktio_system_fd(scheme_rktio, fd, RKTIO_OPEN_NOT_REGFILE);
3585 is_term = rktio_fd_is_terminal(scheme_rktio, rfd);
3586 rktio_forget(scheme_rktio, rfd);
3587
3588 return is_term;
3589 }
3590
scheme_terminal_port_p(int argc,Scheme_Object * argv[])3591 Scheme_Object *scheme_terminal_port_p(int argc, Scheme_Object *argv[])
3592 {
3593 intptr_t fd = 0;
3594 int fd_ok = 0;
3595 Scheme_Object *p;
3596
3597 p = argv[0];
3598
3599 if (SCHEME_INPUT_PORTP(p)) {
3600 Scheme_Input_Port *ip;
3601
3602 ip = scheme_input_port_record(p);
3603
3604 if (ip->closed)
3605 return scheme_false;
3606
3607 if (SAME_OBJ(ip->sub_type, file_input_port_type)) {
3608 fd = MSC_IZE(fileno)((FILE *)((Scheme_Input_File *)ip->port_data)->f);
3609 fd_ok = 1;
3610 }
3611 else if (SAME_OBJ(ip->sub_type, fd_input_port_type)) {
3612 if (rktio_fd_is_terminal(scheme_rktio, ((Scheme_FD *)ip->port_data)->fd))
3613 return scheme_true;
3614 else
3615 return scheme_false;
3616 }
3617 } else if (SCHEME_OUTPUT_PORTP(p)) {
3618 Scheme_Output_Port *op;
3619
3620 op = scheme_output_port_record(p);
3621
3622 if (op->closed)
3623 return scheme_false;
3624
3625 if (SAME_OBJ(op->sub_type, file_output_port_type)) {
3626 fd = MSC_IZE (fileno)((FILE *)((Scheme_Output_File *)op->port_data)->f);
3627 fd_ok = 1;
3628 }
3629 else if (SAME_OBJ(op->sub_type, fd_output_port_type)) {
3630 if (rktio_fd_is_terminal(scheme_rktio, ((Scheme_FD *)op->port_data)->fd))
3631 return scheme_true;
3632 else
3633 return scheme_false;
3634 }
3635 }
3636
3637 if (!fd_ok)
3638 return scheme_false;
3639
3640 return is_fd_terminal(fd) ? scheme_true : scheme_false;
3641 }
3642
maybe_raise_missing_module(char * name,char * filename,char * pre,char * rel,char * post,char * errstr)3643 static void maybe_raise_missing_module(char *name, char *filename, char *pre, char *rel, char *post, char *errstr)
3644 {
3645 Scheme_Object *proc, *a[6];
3646
3647 proc = scheme_get_startup_export("maybe-raise-missing-module");
3648
3649 a[0] = scheme_make_utf8_string(name);
3650 a[1] = scheme_make_utf8_string(filename);
3651 a[2] = scheme_make_utf8_string(pre);
3652 a[3] = scheme_make_utf8_string(rel);
3653 a[4] = scheme_make_utf8_string(post);
3654 a[5] = scheme_make_utf8_string(errstr);
3655
3656 scheme_apply_multi(proc, 6, a);
3657 }
3658
filename_exn(char * name,char * msg,char * filename,int maybe_module_errno)3659 static void filename_exn(char *name, char *msg, char *filename, int maybe_module_errno)
3660 {
3661 char *dir, *drive;
3662 int len;
3663 char *pre, *rel, *post;
3664
3665 len = strlen(filename);
3666
3667 if (scheme_is_relative_path(filename, len, SCHEME_PLATFORM_PATH_KIND)) {
3668 dir = scheme_os_getcwd(NULL, 0, NULL, 1);
3669 drive = NULL;
3670 } else if (scheme_is_complete_path(filename, len, SCHEME_PLATFORM_PATH_KIND)) {
3671 dir = NULL;
3672 drive = NULL;
3673 } else {
3674 dir = NULL;
3675 drive = scheme_getdrive();
3676 }
3677
3678 pre = dir ? "\n in directory: " : (drive ? "\n on drive: " : "");
3679 rel = dir ? dir : (drive ? drive : "");
3680 post = dir ? "" : "";
3681
3682 if (maybe_module_errno && scheme_last_error_is_racket(maybe_module_errno)) {
3683 char buffer[256];
3684 int errkind, errid;
3685
3686 scheme_sprintf(buffer, sizeof(buffer)-1, "%R");
3687 buffer[sizeof(buffer)-1] = 0;
3688
3689 /* Save errors, in case we don't raise missing-module */
3690 errkind = rktio_get_last_error_kind(scheme_rktio);
3691 errid = rktio_get_last_error(scheme_rktio);
3692
3693 maybe_raise_missing_module(name, filename, pre, rel, post, buffer);
3694
3695 /* Restore error, which might have been changed by a scheduler action */
3696 rktio_set_last_error(scheme_rktio, errkind, errid);
3697 }
3698
3699 scheme_raise_exn(MZEXN_FAIL_FILESYSTEM,
3700 "%s: %s\n"
3701 " path: %q%s%q%s\n"
3702 " system error: %R",
3703 name, msg, filename,
3704 pre, rel, post);
3705 }
3706
3707 Scheme_Object *
scheme_do_open_input_file(char * name,int offset,int argc,Scheme_Object * argv[],int internal,int for_module)3708 scheme_do_open_input_file(char *name, int offset, int argc, Scheme_Object *argv[],
3709 int internal, int for_module)
3710 {
3711 char *filename;
3712 int i;
3713 int m_set = 0, mm_set = 0, text_mode = 0;
3714 rktio_fd_t *fd;
3715
3716 if (!SCHEME_PATH_STRINGP(argv[0]))
3717 scheme_wrong_contract(name, "path-string?", 0, argc, argv);
3718
3719 for (i = 1 + offset; argc > i; i++) {
3720 if (!SCHEME_SYMBOLP(argv[i]))
3721 scheme_wrong_contract(name, "symbol?", i, argc, argv);
3722
3723 if (SAME_OBJ(argv[i], text_symbol)) {
3724 text_mode = 1;
3725 m_set++;
3726 } else if (SAME_OBJ(argv[i], binary_symbol)) {
3727 /* This is the default */
3728 m_set++;
3729 } else if (SAME_OBJ(argv[i], module_symbol)) {
3730 mm_set++;
3731 for_module = 1;
3732 } else if (SAME_OBJ(argv[i], scheme_none_symbol)) {
3733 mm_set++;
3734 for_module = 0;
3735 } else {
3736 char *astr;
3737 intptr_t alen;
3738
3739 astr = scheme_make_args_string("other ", i, argc, argv, &alen);
3740 scheme_raise_exn(MZEXN_FAIL_CONTRACT,
3741 "%s: bad mode symbol\n"
3742 " given symbol: %s%t", name,
3743 scheme_make_provided_string(argv[i], 1, NULL),
3744 astr, alen);
3745 }
3746
3747 if ((m_set > 1) || (mm_set > 1)) {
3748 char *astr;
3749 intptr_t alen;
3750
3751 astr = scheme_make_args_string("", -1, argc, argv, &alen);
3752 scheme_raise_exn(MZEXN_FAIL_CONTRACT,
3753 "%s: conflicting or redundant file modes given%t",
3754 name,
3755 astr, alen);
3756 }
3757 }
3758
3759 filename = scheme_expand_string_filename(argv[0],
3760 name,
3761 NULL,
3762 (internal ? 0 : SCHEME_GUARD_FILE_READ));
3763
3764 if (!internal)
3765 scheme_custodian_check_available(NULL, name, "file-stream");
3766
3767 fd = rktio_open(scheme_rktio, filename, (RKTIO_OPEN_READ
3768 | (text_mode ? RKTIO_OPEN_TEXT : 0)));
3769
3770 if (!fd) {
3771 filename_exn(name, "cannot open input file", filename, (for_module ? RKTIO_ERROR_DOES_NOT_EXIST : 0));
3772 return NULL;
3773 }
3774
3775 return make_fd_input_port(fd, scheme_make_path(filename), NULL, internal);
3776 }
3777
3778 Scheme_Object *
scheme_do_open_output_file(char * name,int offset,int argc,Scheme_Object * argv[],int and_read,int internal)3779 scheme_do_open_output_file(char *name, int offset, int argc, Scheme_Object *argv[], int and_read,
3780 int internal)
3781 {
3782 int e_set = 0, m_set = 0, i;
3783 int open_flags = 0, try_replace = 0;
3784 char *filename;
3785 char mode[4];
3786 int typepos;
3787 int perms;
3788 rktio_fd_t *fd;
3789
3790 mode[0] = 'w';
3791 mode[1] = 'b';
3792 mode[2] = 0;
3793 mode[3] = 0;
3794 typepos = 1;
3795 perms = RKTIO_DEFAULT_PERM_BITS;
3796
3797 if (!SCHEME_PATH_STRINGP(argv[0]))
3798 scheme_wrong_contract(name, "path-string?", 0, argc, argv);
3799
3800 for (i = 1 + offset; argc > i; i++) {
3801 if (SCHEME_INTP(argv[i])
3802 && (SCHEME_INT_VAL(argv[i]) >= 0)
3803 && (SCHEME_INT_VAL(argv[i]) <= 65535)) {
3804 perms = SCHEME_INT_VAL(argv[i]);
3805 } else {
3806 if (!SCHEME_SYMBOLP(argv[i]))
3807 scheme_wrong_contract(name, "(or/c symbol? (integer-in 0 65535))", i, argc, argv);
3808
3809 if (SAME_OBJ(argv[i], append_symbol)) {
3810 mode[0] = 'a';
3811 open_flags = RKTIO_OPEN_APPEND;
3812 e_set++;
3813 } else if (SAME_OBJ(argv[i], replace_symbol)) {
3814 try_replace = 1;
3815 e_set++;
3816 } else if (SAME_OBJ(argv[i], truncate_symbol)) {
3817 open_flags = RKTIO_OPEN_TRUNCATE | RKTIO_OPEN_CAN_EXIST;
3818 e_set++;
3819 } else if (SAME_OBJ(argv[i], must_truncate_symbol)) {
3820 open_flags = RKTIO_OPEN_MUST_EXIST | RKTIO_OPEN_TRUNCATE;
3821 e_set++;
3822 } else if (SAME_OBJ(argv[i], truncate_replace_symbol)) {
3823 open_flags = RKTIO_OPEN_TRUNCATE | RKTIO_OPEN_CAN_EXIST;
3824 try_replace = 1;
3825 e_set++;
3826 } else if (SAME_OBJ(argv[i], update_symbol)) {
3827 open_flags = RKTIO_OPEN_MUST_EXIST;
3828 if (typepos == 1) {
3829 mode[2] = mode[1];
3830 typepos = 2;
3831 }
3832 mode[0] = 'r';
3833 mode[1] = '+';
3834 e_set++;
3835 } else if (SAME_OBJ(argv[i], can_update_symbol)) {
3836 open_flags = RKTIO_OPEN_CAN_EXIST;
3837 if (typepos == 1) {
3838 mode[2] = mode[1];
3839 typepos = 2;
3840 }
3841 mode[0] = 'r';
3842 mode[1] = '+';
3843 e_set++;
3844 } else if (SAME_OBJ(argv[i], error_symbol)) {
3845 /* This is the default */
3846 e_set++;
3847 } else if (SAME_OBJ(argv[i], text_symbol)) {
3848 mode[typepos] = 't';
3849 m_set++;
3850 } else if (SAME_OBJ(argv[i], binary_symbol)) {
3851 /* This is the default */
3852 m_set++;
3853 } else {
3854 char *astr;
3855 intptr_t alen;
3856
3857 astr = scheme_make_args_string("other ", i, argc, argv, &alen);
3858 scheme_raise_exn(MZEXN_FAIL_CONTRACT,
3859 "%s: bad mode symbol\n"
3860 " given symbol: : %s%s", name,
3861 scheme_make_provided_string(argv[i], 1, NULL),
3862 astr, alen);
3863 }
3864
3865 if (m_set > 1 || e_set > 1) {
3866 char *astr;
3867 intptr_t alen;
3868
3869 astr = scheme_make_args_string("", -1, argc, argv, &alen);
3870 scheme_raise_exn(MZEXN_FAIL_CONTRACT,
3871 "%s: conflicting or redundant file modes given%t",
3872 name,
3873 astr, alen);
3874 }
3875 }
3876 }
3877
3878 filename = scheme_expand_string_filename(argv[0],
3879 name, NULL,
3880 (internal
3881 ? 0
3882 : (SCHEME_GUARD_FILE_WRITE
3883 | (try_replace
3884 ? SCHEME_GUARD_FILE_DELETE
3885 : 0)
3886 /* append mode: */
3887 | ((mode[0] == 'a')
3888 ? SCHEME_GUARD_FILE_READ
3889 : 0)
3890 /* update mode: */
3891 | ((open_flags & (RKTIO_OPEN_CAN_EXIST | RKTIO_OPEN_MUST_EXIST)
3892 && !(open_flags & (RKTIO_OPEN_TRUNCATE
3893 | RKTIO_OPEN_APPEND))
3894 && !try_replace)
3895 ? SCHEME_GUARD_FILE_READ
3896 : 0))));
3897
3898 scheme_custodian_check_available(NULL, name, "file-stream");
3899
3900 while (1) {
3901 fd = rktio_open_with_create_permissions(scheme_rktio, filename,
3902 (RKTIO_OPEN_WRITE
3903 | open_flags
3904 | (and_read ? RKTIO_OPEN_READ : 0)
3905 | ((mode[1] == 't') ? RKTIO_OPEN_TEXT : 0)),
3906 perms);
3907
3908 if (!fd
3909 && try_replace
3910 && (scheme_last_error_is_racket(RKTIO_ERROR_EXISTS)
3911 || (scheme_last_error_is_racket(RKTIO_ERROR_ACCESS_DENIED)
3912 && rktio_file_exists(scheme_rktio, filename)))) {
3913 /* In replace mode, delete file and try again */
3914 if (!rktio_delete_file(scheme_rktio, filename, scheme_can_enable_write_permission())) {
3915 scheme_raise_exn(MZEXN_FAIL_FILESYSTEM,
3916 "%s: error deleting file\n"
3917 " path: %q\n"
3918 " system error: %R",
3919 name, filename);
3920 }
3921 try_replace = 0;
3922 } else
3923 break;
3924 }
3925
3926 if (!fd) {
3927 if (scheme_last_error_is_racket(RKTIO_ERROR_EXISTS)) {
3928 scheme_raise_exn(MZEXN_FAIL_FILESYSTEM_EXISTS,
3929 "%s: file exists\n"
3930 " path: %q", name, filename);
3931 } else if (scheme_last_error_is_racket(RKTIO_ERROR_IS_A_DIRECTORY)) {
3932 scheme_raise_exn(MZEXN_FAIL_FILESYSTEM_EXISTS,
3933 "%s: path is a directory\n"
3934 " path: %q",
3935 name, filename);
3936 } else
3937 scheme_raise_exn(MZEXN_FAIL_FILESYSTEM,
3938 "%s: cannot open output file\n"
3939 " path: %q\n"
3940 " system error: %R",
3941 name, filename);
3942 }
3943
3944 return make_fd_output_port(fd, scheme_make_path(filename), and_read, -1, NULL);
3945 }
3946
scheme_open_input_file(const char * name,const char * who)3947 Scheme_Object *scheme_open_input_file(const char *name, const char *who)
3948 {
3949 Scheme_Object *a[1];
3950
3951 a[0]= scheme_make_path(name);
3952 return scheme_do_open_input_file((char *)who, 0, 1, a, 0, 0);
3953 }
3954
scheme_open_output_file(const char * name,const char * who)3955 Scheme_Object *scheme_open_output_file(const char *name, const char *who)
3956 {
3957 Scheme_Object *a[2];
3958
3959 a[0]= scheme_make_path(name);
3960 a[1] = truncate_replace_symbol;
3961 return scheme_do_open_output_file((char *)who, 0, 2, a, 0, 0);
3962 }
3963
scheme_open_input_output_file(const char * name,const char * who,Scheme_Object ** oport)3964 Scheme_Object *scheme_open_input_output_file(const char *name, const char *who, Scheme_Object **oport)
3965 {
3966 Scheme_Object *a[2];
3967
3968 a[0]= scheme_make_path(name);
3969 a[1] = truncate_replace_symbol;
3970 scheme_do_open_output_file((char *)who, 0, 2, a, 1, 0);
3971 *oport = scheme_multiple_array[1];
3972 return scheme_multiple_array[0];
3973 }
3974
scheme_open_output_file_with_mode(const char * name,const char * who,int text)3975 Scheme_Object *scheme_open_output_file_with_mode(const char *name, const char *who, int text)
3976 {
3977 Scheme_Object *a[3];
3978
3979 a[0]= scheme_make_path(name);
3980 a[1] = truncate_replace_symbol;
3981 a[2] = (text ? text_symbol : binary_symbol);
3982 return scheme_do_open_output_file((char *)who, 0, 3, a, 0, 0);
3983 }
3984
3985 static Scheme_Object *
do_file_position(const char * who,int argc,Scheme_Object * argv[],int can_false)3986 do_file_position(const char *who, int argc, Scheme_Object *argv[], int can_false)
3987 {
3988 FILE *f;
3989 Scheme_Indexed_String *is;
3990 rktio_fd_t *fd;
3991 int wis;
3992
3993 if (!SCHEME_OUTPUT_PORTP(argv[0]) && !SCHEME_INPUT_PORTP(argv[0]))
3994 scheme_wrong_contract(who, "port?", 0, argc, argv);
3995 if (argc == 2) {
3996 if (!SCHEME_EOFP(argv[1])) {
3997 int ok = 0;
3998
3999 if (SCHEME_INTP(argv[1])) {
4000 ok = (SCHEME_INT_VAL(argv[1]) >= 0);
4001 }
4002
4003 if (SCHEME_BIGNUMP(argv[1])) {
4004 ok = SCHEME_BIGPOS(argv[1]);
4005 }
4006
4007 if (!ok)
4008 scheme_wrong_contract(who, "(or/c exact-nonnegative-integer? eof-object?)", 1, argc, argv);
4009 }
4010 }
4011
4012 f = NULL;
4013 is = NULL;
4014 wis = 0;
4015 fd = NULL;
4016
4017 if (!SCHEME_INPUT_PORTP(argv[0])) {
4018 Scheme_Output_Port *op;
4019
4020 op = scheme_output_port_record(argv[0]);
4021
4022 if (SAME_OBJ(op->sub_type, file_output_port_type)) {
4023 f = ((Scheme_Output_File *)op->port_data)->f;
4024 } else if (SAME_OBJ(op->sub_type, fd_output_port_type)) {
4025 fd = ((Scheme_FD *)op->port_data)->fd;
4026 } else if (SAME_OBJ(op->sub_type, scheme_string_output_port_type)) {
4027 is = (Scheme_Indexed_String *)op->port_data;
4028 wis = 1;
4029 } else if (argc < 2) {
4030 intptr_t pos;
4031 pos = scheme_tell_can_redirect(argv[0], 1);
4032 if (pos < 0) {
4033 if (can_false) return scheme_false;
4034 scheme_raise_exn(MZEXN_FAIL_FILESYSTEM,
4035 "the port's current position is not known\n"
4036 " port: %v",
4037 op);
4038 } else
4039 return scheme_make_integer(pos);
4040 }
4041 } else {
4042 Scheme_Input_Port *ip;
4043
4044 ip = scheme_input_port_record(argv[0]);
4045
4046 if (ip->input_lock)
4047 scheme_wait_input_allowed(ip, 0);
4048
4049 if (SAME_OBJ(ip->sub_type, file_input_port_type)) {
4050 f = ((Scheme_Input_File *)ip->port_data)->f;
4051 } else if (SAME_OBJ(ip->sub_type, fd_input_port_type)) {
4052 fd = ((Scheme_FD *)ip->port_data)->fd;
4053 } else if (SAME_OBJ(ip->sub_type, scheme_string_input_port_type))
4054 is = (Scheme_Indexed_String *)ip->port_data;
4055 else if (argc < 2) {
4056 intptr_t pos;
4057 pos = scheme_tell_can_redirect((Scheme_Object *)ip, 1);
4058 if (pos < 0) {
4059 if (can_false) return scheme_false;
4060 scheme_raise_exn(MZEXN_FAIL_FILESYSTEM,
4061 "the port's current position is not known\n"
4062 " port: %v",
4063 ip);
4064 }
4065 return scheme_make_integer_value(pos);
4066 }
4067 }
4068
4069 if (!f && !fd && !is)
4070 scheme_contract_error(who,
4071 "setting position allowed for file-stream and string ports only",
4072 "port", 1, argv[0],
4073 "position", 1, argv[1],
4074 NULL);
4075
4076 if (argc > 1) {
4077 mzlonglong nll;
4078 int whence;
4079
4080 if (SCHEME_EOFP(argv[1])) {
4081 nll = 0;
4082 whence = SEEK_END;
4083 } else if (scheme_get_long_long_val(argv[1], &nll)) {
4084 whence = SEEK_SET;
4085 if ((mzlonglong)(mz_off_t)nll != nll) {
4086 nll = -1;
4087 }
4088 } else {
4089 whence = SEEK_SET; /* not used */
4090 nll = -1;
4091 }
4092
4093 if (nll < 0) {
4094 scheme_contract_error(who,
4095 "new position is too large",
4096 "port", 1, argv[0],
4097 "position", 1, argv[1],
4098 NULL);
4099 return NULL;
4100 }
4101
4102 if (f) {
4103 if (BIG_OFF_T_IZE(fseeko)(f, nll, whence)) {
4104 scheme_raise_exn(MZEXN_FAIL_FILESYSTEM,
4105 "file-position: position change failed on file\n"
4106 " system error: %e",
4107 errno);
4108 }
4109 } else if (fd) {
4110 if (!SCHEME_INPUT_PORTP(argv[0])) {
4111 flush_fd(scheme_output_port_record(argv[0]), NULL, 0, 0, 0, 0);
4112 }
4113
4114 if (!rktio_set_file_position(scheme_rktio, fd, nll,
4115 ((whence == SEEK_SET)
4116 ? RKTIO_POSITION_FROM_START
4117 : RKTIO_POSITION_FROM_END))) {
4118 scheme_raise_exn(MZEXN_FAIL_FILESYSTEM,
4119 "file-position: position change failed on stream\n"
4120 " system error: %R");
4121 return NULL;
4122 }
4123
4124 if (SCHEME_INPUT_PORTP(argv[0])) {
4125 /* Get rid of buffered data: */
4126 Scheme_FD *sfd;
4127 Scheme_Input_Port *ip;
4128 ip = scheme_input_port_record(argv[0]);
4129 sfd = (Scheme_FD *)ip->port_data;
4130 sfd->bufcount = 0;
4131 sfd->buffpos = 0;
4132 /* 1 means no pending eof, but can set: */
4133 ip->pending_eof = 1;
4134 }
4135 } else {
4136 intptr_t n;
4137
4138 if (whence == SEEK_SET) {
4139 if (!scheme_get_int_val(argv[1], &n)) {
4140 scheme_raise_out_of_memory(NULL, NULL);
4141 }
4142 } else {
4143 n = 0;
4144 }
4145
4146 if (whence == SEEK_END) {
4147 if (wis)
4148 n = is->u.hot;
4149 else
4150 n = is->size;
4151 }
4152 if (wis) {
4153 if (is->index > is->u.hot)
4154 is->u.hot = is->index;
4155 if (is->size < n) {
4156 /* Expand string up to n: */
4157 char *old;
4158
4159 old = is->string;
4160 {
4161 char *ca;
4162 ca = (char *)scheme_malloc_fail_ok(scheme_malloc_atomic, n + 1);
4163 is->string = ca;
4164 }
4165 is->size = n;
4166 memcpy(is->string, old, is->u.hot);
4167 }
4168 if (n > is->u.hot) {
4169 memset(is->string + is->u.hot, 0, n - is->u.hot);
4170 is->u.hot = n;
4171 }
4172 } else {
4173 /* Can't really move past end of read string, but pretend we do: */
4174 if (n > is->size) {
4175 is->u.pos = n;
4176 n = is->size;
4177 } else
4178 is->u.pos = 0;
4179 }
4180 is->index = n;
4181 }
4182
4183 /* Remove any chars saved from peeks: */
4184 if (SCHEME_INPUT_PORTP(argv[0])) {
4185 Scheme_Input_Port *ip;
4186 ip = scheme_input_port_record(argv[0]);
4187 ip->ungotten_count = 0;
4188 if (pipe_char_count(ip->peeked_read)) {
4189 ip->peeked_read = NULL;
4190 ip->peeked_write = NULL;
4191 }
4192 }
4193
4194 return scheme_void;
4195 } else {
4196 mzlonglong pll;
4197 int already_ungot = 0;
4198 if (f) {
4199 pll = BIG_OFF_T_IZE(ftello)(f);
4200 } else if (fd) {
4201 rktio_filesize_t *sz;
4202
4203 sz = rktio_get_file_position(scheme_rktio, fd);
4204 if (!sz) {
4205 pll = do_tell(argv[0], 0);
4206 already_ungot = 1;
4207 } else {
4208 pll = *sz;
4209 free(sz);
4210
4211 if (SCHEME_INPUT_PORTP(argv[0])) {
4212 Scheme_Input_Port *ip;
4213 ip = scheme_input_port_record(argv[0]);
4214 pll -= ((Scheme_FD *)ip->port_data)->bufcount;
4215 if (rktio_fd_is_text_converted(scheme_rktio, fd)) {
4216 /* Correct for CRLF->LF conversion of buffer content */
4217 int bp, bd;
4218 bd = ((Scheme_FD *)ip->port_data)->buffpos;
4219 for (bp = ((Scheme_FD *)ip->port_data)->bufcount; bp--; ) {
4220 if (((Scheme_FD *)ip->port_data)->bufwidths[bp + bd]) {
4221 /* this is a LF converted from CRLF */
4222 pll--;
4223 }
4224 }
4225 pll -= rktio_buffered_byte_count(scheme_rktio, ((Scheme_FD *)ip->port_data)->fd);
4226 }
4227 } else {
4228 Scheme_Output_Port *op;
4229 op = scheme_output_port_record(argv[0]);
4230 pll += ((Scheme_FD *)op->port_data)->bufcount;
4231 }
4232 }
4233 } else if (wis)
4234 pll = is->index;
4235 else {
4236 /* u.pos > index implies we previously moved past the end with file-position */
4237 if (is->u.pos > is->index)
4238 pll = is->u.pos;
4239 else
4240 pll = is->index;
4241 }
4242
4243 /* Back up for un-gotten & peeked chars: */
4244 if (!already_ungot && SCHEME_INPUT_PORTP(argv[0])) {
4245 Scheme_Input_Port *ip;
4246 ip = scheme_input_port_record(argv[0]);
4247 pll -= ip->ungotten_count;
4248 pll -= pipe_char_count(ip->peeked_read);
4249 }
4250
4251 return scheme_make_integer_value_from_long_long(pll);
4252 }
4253 }
4254
4255 Scheme_Object *
scheme_file_position(int argc,Scheme_Object * argv[])4256 scheme_file_position(int argc, Scheme_Object *argv[])
4257 {
4258 return do_file_position("file-position", argc, argv, 0);
4259 }
4260
4261 Scheme_Object *
scheme_file_position_star(int argc,Scheme_Object * argv[])4262 scheme_file_position_star(int argc, Scheme_Object *argv[])
4263 {
4264 return do_file_position("file-position*", argc, argv, 1);
4265 }
4266
scheme_file_truncate(int argc,Scheme_Object * argv[])4267 Scheme_Object *scheme_file_truncate(int argc, Scheme_Object *argv[])
4268 {
4269 mzlonglong nll;
4270 Scheme_Output_Port *op;
4271 rktio_fd_t *fd;
4272 int free_fd = 0, ok;
4273
4274 if (!SCHEME_OUTPUT_PORTP(argv[0])
4275 || SCHEME_FALSEP(scheme_file_stream_port_p(1, argv)))
4276 scheme_wrong_contract("file-truncate", "(and/c output-port? file-stream-port?)", 0, argc, argv);
4277
4278 if (!(SCHEME_INTP(argv[1]) && (SCHEME_INT_VAL(argv[1]) >= 0))
4279 && !(SCHEME_BIGNUMP(argv[1]) && SCHEME_BIGPOS(argv[1])))
4280 scheme_wrong_contract("file-truncate", "exact-nonnegative-integer?", 1, argc, argv);
4281
4282 if (!scheme_get_long_long_val(argv[1], &nll)) {
4283 scheme_raise_exn(MZEXN_FAIL_FILESYSTEM,
4284 "file-truncate: size change failed\n"
4285 " reason: size too large");
4286 }
4287
4288 op = scheme_output_port_record(argv[0]);
4289
4290 if (SAME_OBJ(op->sub_type, file_output_port_type)) {
4291 intptr_t sfd;
4292 sfd = MSC_IZE (fileno)((FILE *)((Scheme_Output_File *)op->port_data)->f);
4293 fd = rktio_system_fd(scheme_rktio, sfd, RKTIO_OPEN_NOT_REGFILE);
4294 free_fd = 1;
4295 } else if (SAME_OBJ(op->sub_type, fd_output_port_type)) {
4296 fd = ((Scheme_FD *)op->port_data)->fd;
4297 } else
4298 return scheme_void;
4299
4300 flush_fd(scheme_output_port_record(argv[0]), NULL, 0, 0, 0, 0);
4301
4302 ok = rktio_set_file_size(scheme_rktio, fd, nll);
4303
4304 if (free_fd) rktio_forget(scheme_rktio, fd);
4305
4306 if (!ok) {
4307 scheme_raise_exn(MZEXN_FAIL_FILESYSTEM,
4308 "file-truncate: size change failed\n"
4309 " system error: %R");
4310 }
4311
4312 return scheme_void;
4313 }
4314
scheme_set_file_position(Scheme_Object * port,intptr_t pos)4315 intptr_t scheme_set_file_position(Scheme_Object *port, intptr_t pos)
4316 {
4317 if (pos >= 0) {
4318 Scheme_Object *a[2];
4319
4320 a[0] = port;
4321 a[1] = scheme_make_integer(pos);
4322 (void)scheme_file_position(2, a);
4323 return 0;
4324 } else {
4325 Scheme_Object *n;
4326 n = scheme_file_position(1, &port);
4327 return SCHEME_INT_VAL(n);
4328 }
4329 }
4330
4331 Scheme_Object *
scheme_file_buffer(int argc,Scheme_Object * argv[])4332 scheme_file_buffer(int argc, Scheme_Object *argv[])
4333 {
4334 Scheme_Port *p = NULL;
4335
4336 if (!SCHEME_OUTPUT_PORTP(argv[0]) && !SCHEME_INPUT_PORTP(argv[0]))
4337 scheme_wrong_contract("file-stream-buffer-mode", "port?", 0, argc, argv);
4338
4339 p = scheme_port_record(argv[0]);
4340
4341 if (argc == 1) {
4342 Scheme_Buffer_Mode_Fun bm;
4343
4344 bm = p->buffer_mode_fun;
4345 if (bm) {
4346 switch (bm(p, -1)) {
4347 case MZ_FLUSH_NEVER:
4348 return scheme_block_symbol;
4349 case MZ_FLUSH_BY_LINE:
4350 return scheme_line_symbol;
4351 case MZ_FLUSH_ALWAYS:
4352 return scheme_none_symbol;
4353 }
4354 }
4355
4356 return scheme_false;
4357 } else {
4358 Scheme_Object *s = argv[1];
4359 Scheme_Buffer_Mode_Fun bm;
4360
4361 if (!SAME_OBJ(s, scheme_block_symbol)
4362 && !SAME_OBJ(s, scheme_line_symbol)
4363 && !SAME_OBJ(s, scheme_none_symbol))
4364 scheme_wrong_contract("file-stream-buffer-mode", "(or/c 'none 'line 'block)", 1, argc, argv);
4365
4366 if (SCHEME_INPUT_PORTP(argv[0]) && SAME_OBJ(s, scheme_line_symbol))
4367 scheme_contract_error("file-stream-buffer-mode",
4368 "'line buffering not supported for an input port",
4369 "port", 1, argv[0],
4370 NULL);
4371
4372 bm = p->buffer_mode_fun;
4373 if (bm) {
4374 int mode;
4375 if (SAME_OBJ(s, scheme_block_symbol))
4376 mode = MZ_FLUSH_NEVER;
4377 else if (SAME_OBJ(s, scheme_line_symbol))
4378 mode = MZ_FLUSH_BY_LINE;
4379 else
4380 mode = MZ_FLUSH_ALWAYS;
4381
4382 bm(p, mode);
4383 } else {
4384 scheme_contract_error("file-stream-buffer-mode",
4385 "cannot set buffer mode on port",
4386 "port", 1, argv[0],
4387 NULL);
4388 }
4389
4390 return scheme_void;
4391 }
4392 }
4393
check_already_closed(const char * name,Scheme_Object * p)4394 static void check_already_closed(const char *name, Scheme_Object *p)
4395 {
4396 int is_closed;
4397 if (SCHEME_INPUT_PORTP(p)) {
4398 is_closed = scheme_input_port_record(p)->closed;
4399 } else {
4400 is_closed = scheme_output_port_record(p)->closed;
4401 }
4402 if (is_closed) {
4403 scheme_contract_error(name,
4404 "port is closed",
4405 "port", 1, p,
4406 NULL);
4407 }
4408 }
4409
scheme_file_try_lock(int argc,Scheme_Object ** argv)4410 Scheme_Object *scheme_file_try_lock(int argc, Scheme_Object **argv)
4411 {
4412 rktio_fd_t *rfd = NULL;
4413 intptr_t fd;
4414 int writer = 0, r;
4415
4416 if (!scheme_get_port_rktio_file_descriptor(argv[0], &rfd)
4417 && !scheme_get_port_file_descriptor(argv[0], &fd))
4418 scheme_wrong_contract("port-try-file-lock?", "file-stream-port?", 0, argc, argv);
4419
4420 if (SCHEME_SYMBOLP(argv[1]) && !SCHEME_SYM_WEIRDP(argv[1])) {
4421 if (!strcmp(SCHEME_SYM_VAL(argv[1]), "exclusive"))
4422 writer = 1;
4423 else if (!strcmp(SCHEME_SYM_VAL(argv[1]), "shared"))
4424 writer = 0;
4425 else
4426 writer = -1;
4427 } else
4428 writer = -1;
4429
4430 if (writer == -1)
4431 scheme_wrong_contract("port-try-file-lock?", "(or/c 'shared 'exclusive)", 1, argc, argv);
4432
4433 if (writer && !SCHEME_OUTPORTP(argv[0]))
4434 scheme_contract_error("port-try-file-lock?",
4435 "port for 'exclusive locking is not an output port",
4436 "port", 1, argv[0],
4437 NULL);
4438 else if (!writer && !SCHEME_INPORTP(argv[0]))
4439 scheme_contract_error("port-try-file-lock?",
4440 "port for 'shared locking is not an input port",
4441 "port", 1, argv[0],
4442 NULL);
4443
4444 check_already_closed("port-try-file-lock?", argv[0]);
4445
4446 if (!rfd) {
4447 rfd = rktio_system_fd(scheme_rktio, fd, RKTIO_OPEN_READ | RKTIO_OPEN_WRITE | RKTIO_OPEN_NOT_REGFILE);
4448 r = rktio_file_lock_try(scheme_rktio, rfd, writer);
4449 rktio_forget(scheme_rktio, rfd);
4450 } else
4451 r = rktio_file_lock_try(scheme_rktio, rfd, writer);
4452
4453 if (r == RKTIO_LOCK_ACQUIRED)
4454 return scheme_true;
4455
4456 if (r == RKTIO_LOCK_ERROR) {
4457 scheme_raise_exn(MZEXN_FAIL_FILESYSTEM,
4458 "port-try-file-lock?: error getting file %s lock\n"
4459 " system error: %R",
4460 (writer ? "exclusive" : "shared"));
4461 }
4462
4463 return scheme_false;
4464 }
4465
scheme_file_unlock(int argc,Scheme_Object ** argv)4466 Scheme_Object *scheme_file_unlock(int argc, Scheme_Object **argv)
4467 {
4468 intptr_t fd;
4469 rktio_fd_t *rfd = NULL;
4470 int r;
4471
4472 if (!scheme_get_port_rktio_file_descriptor(argv[0], &rfd)
4473 && !scheme_get_port_file_descriptor(argv[0], &fd))
4474 scheme_wrong_contract("port-file-unlock", "file-stream-port?", 0, argc, argv);
4475
4476 check_already_closed("port-file-unlock", argv[0]);
4477
4478 if (!rfd) {
4479 rfd = rktio_system_fd(scheme_rktio, fd, RKTIO_OPEN_READ | RKTIO_OPEN_WRITE | RKTIO_OPEN_NOT_REGFILE);
4480 r = rktio_file_unlock(scheme_rktio, rfd);
4481 rktio_forget(scheme_rktio, rfd);
4482 } else
4483 r = rktio_file_unlock(scheme_rktio, rfd);
4484
4485 if (!r) {
4486 scheme_raise_exn(MZEXN_FAIL_FILESYSTEM,
4487 "port-file-unlock: error unlocking file\n"
4488 " system error: %R");
4489 }
4490
4491 return scheme_void;
4492 }
4493
4494 /*========================================================================*/
4495 /* filesystem change events */
4496 /*========================================================================*/
4497
filesystem_change_evt_fnl(void * fc,void * data)4498 static void filesystem_change_evt_fnl(void *fc, void *data)
4499 {
4500 scheme_filesystem_change_evt_cancel((Scheme_Object *)fc, NULL);
4501 }
4502
scheme_filesystem_change_evt(Scheme_Object * path,int flags,int signal_errs)4503 Scheme_Object *scheme_filesystem_change_evt(Scheme_Object *path, int flags, int signal_errs)
4504 {
4505 char *filename;
4506 rktio_fs_change_t *rfc;
4507
4508 filename = scheme_expand_string_filename(path,
4509 "filesystem-change-evt",
4510 NULL,
4511 SCHEME_GUARD_FILE_EXISTS);
4512
4513 rfc = rktio_fs_change(scheme_rktio, filename, scheme_semaphore_fd_set);
4514
4515 if (!rfc
4516 && !(rktio_fs_change_properties(scheme_rktio) & RKTIO_FS_CHANGE_FILE_LEVEL)
4517 && scheme_file_exists(filename)) {
4518 Scheme_Object *base;
4519 int is_dir;
4520 char *try_filename;
4521 (void)scheme_split_path(filename, strlen(filename), &base, &is_dir, SCHEME_PLATFORM_PATH_KIND);
4522 try_filename = scheme_expand_string_filename(base,
4523 "filesystem-change-evt",
4524 NULL,
4525 SCHEME_GUARD_FILE_EXISTS);
4526 rfc = rktio_fs_change(scheme_rktio, try_filename, scheme_semaphore_fd_set);
4527 }
4528
4529 if (!rfc) {
4530 if (signal_errs) {
4531 if (scheme_last_error_is_racket(RKTIO_ERROR_UNSUPPORTED)) {
4532 if (signal_errs)
4533 scheme_raise_exn(MZEXN_FAIL_UNSUPPORTED,
4534 "filesystem-change-evt: " NOT_SUPPORTED_STR "\n"
4535 " path: %q\n",
4536 filename);
4537 } else {
4538 filename_exn("filesystem-change-evt", "error generating event", filename, 0);
4539 }
4540 }
4541
4542 return NULL;
4543 }
4544
4545 {
4546 Scheme_Filesystem_Change_Evt *fc;
4547 Scheme_Custodian_Reference *mref;
4548
4549 fc = MALLOC_ONE_TAGGED(Scheme_Filesystem_Change_Evt);
4550 fc->so.type = scheme_filesystem_change_evt_type;
4551 fc->rfc = rfc;
4552
4553 mref = scheme_add_managed(NULL, (Scheme_Object *)fc, scheme_filesystem_change_evt_cancel, NULL, 1);
4554 fc->mref = mref;
4555
4556 scheme_add_finalizer(fc, filesystem_change_evt_fnl, NULL);
4557
4558 return (Scheme_Object *)fc;
4559 }
4560 }
4561
scheme_filesystem_change_evt_cancel(Scheme_Object * evt,void * ignored_data)4562 void scheme_filesystem_change_evt_cancel(Scheme_Object *evt, void *ignored_data)
4563 {
4564 Scheme_Filesystem_Change_Evt *fc = (Scheme_Filesystem_Change_Evt *)evt;
4565
4566 if (fc->rfc) {
4567 rktio_fs_change_forget(scheme_rktio, fc->rfc);
4568 fc->rfc = NULL;
4569 }
4570
4571 if (fc->mref) {
4572 scheme_remove_managed(fc->mref, (Scheme_Object *)fc);
4573 fc->mref = NULL;
4574 }
4575 }
4576
filesystem_change_evt_ready(Scheme_Object * evt,Scheme_Schedule_Info * sinfo)4577 static int filesystem_change_evt_ready(Scheme_Object *evt, Scheme_Schedule_Info *sinfo)
4578 {
4579 Scheme_Filesystem_Change_Evt *fc = (Scheme_Filesystem_Change_Evt *)evt;
4580
4581 if (!fc->rfc)
4582 return 1;
4583
4584 if (rktio_poll_fs_change_ready(scheme_rktio, fc->rfc))
4585 return 1;
4586
4587 return 0;
4588 }
4589
filesystem_change_evt_need_wakeup(Scheme_Object * evt,void * fds)4590 static void filesystem_change_evt_need_wakeup(Scheme_Object *evt, void *fds)
4591 {
4592 Scheme_Filesystem_Change_Evt *fc = (Scheme_Filesystem_Change_Evt *)evt;
4593
4594 if (fc->rfc)
4595 rktio_poll_add_fs_change(scheme_rktio, fc->rfc, fds);
4596 }
4597
scheme_fs_change_properties(int * _supported,int * _scalable,int * _low_latency,int * _file_level)4598 void scheme_fs_change_properties(int *_supported, int *_scalable, int *_low_latency, int *_file_level)
4599 {
4600 int props;
4601
4602 props = rktio_fs_change_properties(scheme_rktio);
4603 if ((props & RKTIO_FS_CHANGE_NEED_LTPS) && !scheme_semaphore_fd_set)
4604 props = 0;
4605
4606 *_supported = ((props & RKTIO_FS_CHANGE_SUPPORTED) ? 1 : 0);
4607 *_scalable = ((props & RKTIO_FS_CHANGE_SCALABLE) ? 1 : 0);
4608 *_low_latency = ((props & RKTIO_FS_CHANGE_LOW_LATENCY) ? 1 : 0);
4609 *_file_level = ((props & RKTIO_FS_CHANGE_FILE_LEVEL) ? 1 : 0);
4610 }
4611
4612 /*========================================================================*/
4613 /* FILE input ports */
4614 /*========================================================================*/
4615
4616 static int
file_byte_ready(Scheme_Input_Port * port)4617 file_byte_ready (Scheme_Input_Port *port)
4618 {
4619 return 1;
4620 }
4621
file_get_string(Scheme_Input_Port * port,char * buffer,intptr_t offset,intptr_t size,int nonblock,Scheme_Object * unless_evt)4622 static intptr_t file_get_string(Scheme_Input_Port *port,
4623 char *buffer, intptr_t offset, intptr_t size,
4624 int nonblock,
4625 Scheme_Object *unless_evt)
4626 {
4627 FILE *fp;
4628 Scheme_Input_File *fip;
4629 int c;
4630
4631 fip = (Scheme_Input_File *)port->port_data;
4632 fp = fip->f;
4633
4634 c = fread(buffer XFORM_OK_PLUS offset, 1, size, fp);
4635
4636 if (c <= 0) {
4637 if (!feof(fp)) {
4638 scheme_raise_exn(MZEXN_FAIL_FILESYSTEM,
4639 "error reading from file port\n"
4640 " port: %V\n"
4641 " system error: %e",
4642 port->name, errno);
4643 return 0;
4644 } else
4645 c = EOF;
4646 #ifndef DONT_CLEAR_FILE_EOF
4647 clearerr(fp);
4648 #endif
4649 }
4650
4651 return c;
4652 }
4653
4654 static void
file_close_input(Scheme_Input_Port * port)4655 file_close_input(Scheme_Input_Port *port)
4656 {
4657 Scheme_Input_File *fip;
4658
4659 fip = (Scheme_Input_File *)port->port_data;
4660
4661 fclose(fip->f);
4662 }
4663
4664 static void
file_need_wakeup(Scheme_Input_Port * port,void * fds)4665 file_need_wakeup(Scheme_Input_Port *port, void *fds)
4666 {
4667 }
4668
4669 static int
file_buffer_mode(Scheme_Port * p,int mode)4670 file_buffer_mode(Scheme_Port *p, int mode)
4671 {
4672 FILE *f;
4673 int bad;
4674
4675 if (mode < 0)
4676 return -1; /* unknown mode */
4677
4678 if (SCHEME_INPORTP((Scheme_Object *)p)) {
4679 Scheme_Input_Port *ip = (Scheme_Input_Port *)p;
4680 f = ((Scheme_Output_File *)ip->port_data)->f;
4681 } else {
4682 Scheme_Output_Port *op = (Scheme_Output_Port *)p;
4683 f = ((Scheme_Output_File *)op->port_data)->f;
4684 }
4685
4686 if (mode == MZ_FLUSH_NEVER)
4687 bad = setvbuf(f, NULL, _IOFBF, 0);
4688 else if (mode == MZ_FLUSH_BY_LINE)
4689 bad = setvbuf(f, NULL, _IOLBF, 0);
4690 else
4691 bad = setvbuf(f, NULL, _IONBF, 0);
4692
4693 if (bad) {
4694 scheme_raise_exn(MZEXN_FAIL_FILESYSTEM,
4695 "file-stream-buffer-mode: error changing buffering\n"
4696 " system error: %e",
4697 errno);
4698 }
4699
4700 return mode;
4701 }
4702
4703
4704 static Scheme_Object *
_scheme_make_named_file_input_port(FILE * fp,Scheme_Object * name,int regfile)4705 _scheme_make_named_file_input_port(FILE *fp, Scheme_Object *name, int regfile)
4706 {
4707 Scheme_Input_Port *ip;
4708 Scheme_Input_File *fip;
4709
4710 if (!fp)
4711 scheme_signal_error("make-file-input-port(internal): "
4712 "null file pointer");
4713
4714 fip = MALLOC_ONE_RT(Scheme_Input_File);
4715 #ifdef MZTAG_REQUIRED
4716 fip->type = scheme_rt_input_file;
4717 #endif
4718
4719 fip->f = fp;
4720
4721 ip = scheme_make_input_port(file_input_port_type,
4722 fip,
4723 name,
4724 file_get_string,
4725 NULL,
4726 scheme_progress_evt_via_get,
4727 scheme_peeked_read_via_get,
4728 file_byte_ready,
4729 file_close_input,
4730 file_need_wakeup,
4731 1);
4732 ip->p.buffer_mode_fun = file_buffer_mode;
4733
4734 return (Scheme_Object *)ip;
4735 }
4736
4737 Scheme_Object *
scheme_make_named_file_input_port(FILE * fp,Scheme_Object * name)4738 scheme_make_named_file_input_port(FILE *fp, Scheme_Object *name)
4739 {
4740 return _scheme_make_named_file_input_port(fp, name, 0);
4741 }
4742
4743 Scheme_Object *
scheme_make_file_input_port(FILE * fp)4744 scheme_make_file_input_port(FILE *fp)
4745 {
4746 return scheme_make_named_file_input_port(fp, scheme_intern_symbol("file"));
4747 }
4748
4749 /*========================================================================*/
4750 /* fd input ports */
4751 /*========================================================================*/
4752
4753 static int
fd_byte_ready(Scheme_Input_Port * port)4754 fd_byte_ready (Scheme_Input_Port *port)
4755 {
4756 Scheme_FD *fip = (Scheme_FD *)port->port_data;
4757
4758 if (port->closed || rktio_fd_is_regular_file(scheme_rktio, fip->fd))
4759 return 1;
4760
4761 if (fip->bufcount)
4762 return 1;
4763 else {
4764 if (rktio_poll_read_ready(scheme_rktio, fip->fd))
4765 return 1;
4766 else
4767 return 0;
4768 }
4769 }
4770
4771 static void
fd_need_wakeup(Scheme_Input_Port * port,void * fds)4772 fd_need_wakeup(Scheme_Input_Port *port, void *fds)
4773 {
4774 Scheme_FD *fip = (Scheme_FD *)port->port_data;
4775
4776 rktio_poll_add(scheme_rktio, fip->fd, fds, RKTIO_POLL_READ);
4777 }
4778
4779 MZ_DO_NOT_INLINE(static intptr_t fd_get_string_slow(Scheme_Input_Port *port,
4780 char *buffer, intptr_t offset, intptr_t size,
4781 int nonblock,
4782 Scheme_Object *unless));
4783
fd_get_string_slow(Scheme_Input_Port * port,char * buffer,intptr_t offset,intptr_t size,int nonblock,Scheme_Object * unless)4784 static intptr_t fd_get_string_slow(Scheme_Input_Port *port,
4785 char *buffer, intptr_t offset, intptr_t size,
4786 int nonblock,
4787 Scheme_Object *unless)
4788 {
4789 Scheme_FD *fip;
4790 intptr_t bc;
4791
4792 fip = (Scheme_FD *)port->port_data;
4793
4794 while (1) {
4795 /* Loop until a read succeeds. */
4796 int none_avail = 0, ext_target;
4797 intptr_t target_size, target_offset;
4798 char *target;
4799 Scheme_Object *sema;
4800
4801 /* If no chars appear to be ready, go to sleep. */
4802 while (!fd_byte_ready(port)) {
4803 if (nonblock > 0)
4804 return 0;
4805
4806 sema = scheme_rktio_fd_to_semaphore(fip->fd, MZFD_CREATE_READ);
4807
4808 if (sema)
4809 scheme_wait_sema(sema, nonblock ? -1 : 0);
4810 else
4811 scheme_block_until_unless((Scheme_Ready_Fun)fd_byte_ready,
4812 (Scheme_Needs_Wakeup_Fun)fd_need_wakeup,
4813 (Scheme_Object *)port,
4814 0.0, unless,
4815 nonblock);
4816
4817 scheme_wait_input_allowed(port, nonblock);
4818
4819 if (scheme_unless_ready(unless))
4820 return SCHEME_UNLESS_READY;
4821 }
4822
4823 if (port->closed) {
4824 /* Another thread closed the input port while we were waiting. */
4825 /* Call scheme_getc to signal the error */
4826 scheme_get_byte((Scheme_Object *)port);
4827 }
4828
4829 if ((size >= MZPORT_FD_DIRECT_THRESHOLD) && (fip->flush != MZ_FLUSH_ALWAYS)) {
4830 ext_target = 1;
4831 target = buffer;
4832 target_offset = offset;
4833 target_size = size;
4834 } else {
4835 ext_target = 0;
4836 target = (char *)fip->buffer;
4837 target_offset = 0;
4838 if (fip->flush == MZ_FLUSH_ALWAYS)
4839 target_size = 1;
4840 else
4841 target_size = MZPORT_FD_BUFFSIZE;
4842 }
4843
4844 if (rktio_fd_is_text_converted(scheme_rktio, fip->fd)) {
4845 /* Always read into the port buffer so that `bufwidths` can be
4846 filled in parallel to the buffer. */
4847 ext_target = 0;
4848 target = fip->buffer;
4849 if (fip->flush == MZ_FLUSH_ALWAYS)
4850 target_size = 1;
4851 else
4852 target_size = MZPORT_FD_BUFFSIZE;
4853
4854 bc = rktio_read_converted(scheme_rktio, fip->fd, fip->buffer, target_size, fip->bufwidths);
4855 } else {
4856 bc = rktio_read(scheme_rktio, fip->fd, target + target_offset, target_size);
4857 }
4858
4859 if (bc == 0)
4860 none_avail = 1;
4861 else if (bc == RKTIO_READ_EOF)
4862 bc = 0; /* EOF */
4863 else if (bc == RKTIO_READ_ERROR)
4864 bc = -1; /* error */
4865
4866 if (!none_avail) {
4867 if (ext_target && (bc > 0)) {
4868 return bc;
4869 }
4870
4871 fip->bufcount = bc;
4872
4873 if (fip->bufcount < 0) {
4874 fip->bufcount = 0;
4875 fip->buffpos = 0;
4876 scheme_raise_exn(MZEXN_FAIL_FILESYSTEM,
4877 "error reading from stream port\n"
4878 " port: %V\n"
4879 " system error: %R",
4880 port->name);
4881 return 0;
4882 }
4883
4884 if (!fip->bufcount) {
4885 if (rktio_buffered_byte_count(scheme_rktio, fip->fd)) {
4886 /* maybe have a CR pending for text conversion, so maybe keep trying */
4887 if (nonblock > 0)
4888 return 0;
4889 } else {
4890 fip->buffpos = 0;
4891 return EOF;
4892 }
4893 } else {
4894 bc = ((size <= fip->bufcount)
4895 ? size
4896 : fip->bufcount);
4897
4898 memcpy(buffer + offset, fip->buffer, bc);
4899 fip->buffpos = bc;
4900 fip->bufcount -= bc;
4901
4902 return bc;
4903 }
4904 } else if (nonblock > 0) {
4905 return 0;
4906 }
4907 }
4908 }
4909
fd_get_string(Scheme_Input_Port * port,char * buffer,intptr_t offset,intptr_t size,int nonblock,Scheme_Object * unless)4910 static intptr_t fd_get_string(Scheme_Input_Port *port,
4911 char *buffer, intptr_t offset, intptr_t size,
4912 int nonblock,
4913 Scheme_Object *unless)
4914 XFORM_ASSERT_NO_CONVERSION
4915 {
4916 Scheme_FD *fip;
4917 intptr_t bc;
4918
4919 /* Buffer-reading fast path is designed to avoid GC,
4920 and thus avoid MZ_PRECISE_GC instrumentation. */
4921
4922 if (unless && scheme_unless_ready(unless))
4923 return SCHEME_UNLESS_READY;
4924
4925 fip = (Scheme_FD *)port->port_data;
4926
4927 if (fip->bufcount) {
4928 if (size == 1) {
4929 buffer[offset] = fip->buffer[fip->buffpos++];
4930 --fip->bufcount;
4931 return 1;
4932 } else {
4933 bc = ((size <= fip->bufcount)
4934 ? size
4935 : fip->bufcount);
4936
4937 memcpy(buffer + offset, fip->buffer + fip->buffpos, bc);
4938 fip->buffpos += bc;
4939 fip->bufcount -= bc;
4940
4941 return bc;
4942 }
4943 } else {
4944 if ((nonblock == 2) && (fip->flush == MZ_FLUSH_ALWAYS))
4945 return 0;
4946
4947 return fd_get_string_slow(port, buffer, offset, size, nonblock, unless);
4948 }
4949 }
4950
4951 static void
fd_close_input(Scheme_Input_Port * port)4952 fd_close_input(Scheme_Input_Port *port)
4953 {
4954 Scheme_FD *fip;
4955 int rc;
4956
4957 fip = (Scheme_FD *)port->port_data;
4958
4959 rc = adj_refcount(fip->refcount, -1);
4960 if (!rc) {
4961 (void)scheme_rktio_fd_to_semaphore(fip->fd, MZFD_REMOVE);
4962 rktio_close(scheme_rktio, fip->fd);
4963 } else
4964 rktio_forget(scheme_rktio, fip->fd);
4965 }
4966
4967 static void
fd_init_close_input(Scheme_Input_Port * port)4968 fd_init_close_input(Scheme_Input_Port *port)
4969 {
4970 /* never actually opened! */
4971 }
4972
fd_input_buffer_mode(Scheme_Port * p,int mode)4973 static int fd_input_buffer_mode(Scheme_Port *p, int mode)
4974 {
4975 Scheme_FD *fd;
4976 Scheme_Input_Port *ip = (Scheme_Input_Port *)p;
4977
4978 fd = (Scheme_FD *)ip->port_data;
4979
4980 if (mode < 0) {
4981 return fd->flush;
4982 } else {
4983 fd->flush = mode;
4984 return mode;
4985 }
4986 }
4987
4988 static Scheme_Object *
make_fd_input_port(rktio_fd_t * fd,Scheme_Object * name,int * refcount,int internal)4989 make_fd_input_port(rktio_fd_t *fd, Scheme_Object *name, int *refcount, int internal)
4990 {
4991 Scheme_Input_Port *ip;
4992 Scheme_FD *fip;
4993 char *bfr;
4994 int start_closed = 0;
4995
4996 fip = MALLOC_ONE_RT(Scheme_FD);
4997 #ifdef MZTAG_REQUIRED
4998 fip->type = scheme_rt_input_fd;
4999 #endif
5000
5001 bfr = (char *)scheme_malloc_atomic(MZPORT_FD_BUFFSIZE);
5002 fip->buffer = bfr;
5003 if (rktio_fd_is_text_converted(scheme_rktio, fd)) {
5004 char *bws;
5005 bws = scheme_malloc_atomic(MZPORT_FD_BUFFSIZE);
5006 fip->bufwidths = bws;
5007 }
5008
5009 fip->fd = fd;
5010 fip->bufcount = 0;
5011
5012 if (refcount) {
5013 fip->refcount = refcount;
5014 if (!adj_refcount(refcount, 1)) {
5015 /* fd is already closed! */
5016 start_closed = 1;
5017 rktio_forget(scheme_rktio, fd);
5018 fip->fd = NULL;
5019 }
5020 }
5021
5022 fip->flush = MZ_FLUSH_NEVER;
5023
5024 ip = scheme_make_input_port(fd_input_port_type,
5025 fip,
5026 name,
5027 fd_get_string,
5028 NULL,
5029 scheme_progress_evt_via_get,
5030 scheme_peeked_read_via_get,
5031 fd_byte_ready,
5032 (start_closed
5033 ? fd_init_close_input
5034 : fd_close_input),
5035 fd_need_wakeup,
5036 !internal);
5037 ip->p.buffer_mode_fun = fd_input_buffer_mode;
5038
5039 ip->pending_eof = 1; /* means that pending EOFs should be tracked */
5040
5041 if (start_closed)
5042 scheme_close_input_port((Scheme_Object *)ip);
5043
5044 return (Scheme_Object *)ip;
5045 }
5046
5047 Scheme_Object *
scheme_make_fd_input_port(int fd,Scheme_Object * name,int regfile,int textmode)5048 scheme_make_fd_input_port(int fd, Scheme_Object *name, int regfile, int textmode)
5049 {
5050 rktio_fd_t *rfd;
5051
5052 rfd = rktio_system_fd(scheme_rktio,
5053 fd,
5054 (RKTIO_OPEN_READ
5055 | (regfile
5056 ? RKTIO_OPEN_REGFILE
5057 : RKTIO_OPEN_NOT_REGFILE)
5058 | (textmode ? RKTIO_OPEN_TEXT : 0)));
5059
5060 return make_fd_input_port(rfd, name, NULL, 0);
5061 }
5062
5063 Scheme_Object *
scheme_make_rktio_fd_input_port(rktio_fd_t * rfd,Scheme_Object * name)5064 scheme_make_rktio_fd_input_port(rktio_fd_t *rfd, Scheme_Object *name)
5065 {
5066 return make_fd_input_port(rfd, name, NULL, 0);
5067 }
5068
5069 /*========================================================================*/
5070 /* FILE output ports */
5071 /*========================================================================*/
5072
5073 /* Note that we don't try to implement non-blocking writes on FILE
5074 objects. In Unix, a program could conceiveably open a named pipe
5075 and block on it. */
5076
file_flush(Scheme_Output_Port * port)5077 static void file_flush(Scheme_Output_Port *port)
5078 {
5079 if (fflush(((Scheme_Output_File *)port->port_data)->f)) {
5080 scheme_raise_exn(MZEXN_FAIL_FILESYSTEM,
5081 "error flushing file port\n"
5082 " system error: %e",
5083 errno);
5084 }
5085 }
5086
5087 static intptr_t
file_write_string(Scheme_Output_Port * port,const char * str,intptr_t d,intptr_t llen,int rarely_block,int enable_break)5088 file_write_string(Scheme_Output_Port *port,
5089 const char *str, intptr_t d, intptr_t llen,
5090 int rarely_block, int enable_break)
5091 {
5092 FILE *fp;
5093 intptr_t len = llen;
5094
5095 fp = ((Scheme_Output_File *)port->port_data)->f;
5096
5097 if (!len) {
5098 file_flush(port);
5099 return 0;
5100 }
5101
5102 if (fwrite(str XFORM_OK_PLUS d, len, 1, fp) != 1) {
5103 scheme_raise_exn(MZEXN_FAIL_FILESYSTEM,
5104 "error writing to file port\n"
5105 " system error: %e",
5106 errno);
5107 return 0;
5108 }
5109
5110 if (rarely_block) {
5111 file_flush(port);
5112 } else {
5113 while (len--) {
5114 if (str[d] == '\n' || str[d] == '\r') {
5115 file_flush(port);
5116 break;
5117 }
5118 d++;
5119 }
5120 }
5121
5122 return llen;
5123 }
5124
5125 static void
file_close_output(Scheme_Output_Port * port)5126 file_close_output(Scheme_Output_Port *port)
5127 {
5128 Scheme_Output_File *fop = (Scheme_Output_File *)port->port_data;
5129 FILE *fp = fop->f;
5130
5131 fclose(fp);
5132 }
5133
5134 Scheme_Object *
scheme_make_file_output_port(FILE * fp)5135 scheme_make_file_output_port(FILE *fp)
5136 {
5137 Scheme_Output_File *fop;
5138 Scheme_Output_Port *op;
5139
5140 if (!fp)
5141 scheme_signal_error("make-file-out-port(internal): "
5142 "null file pointer");
5143
5144 fop = MALLOC_ONE_RT(Scheme_Output_File);
5145 #ifdef MZTAG_REQUIRED
5146 fop->type = scheme_rt_output_file;
5147 #endif
5148
5149 fop->f = fp;
5150
5151 op = scheme_make_output_port(file_output_port_type,
5152 fop,
5153 scheme_intern_symbol("file"),
5154 scheme_write_evt_via_write,
5155 file_write_string,
5156 NULL,
5157 file_close_output,
5158 NULL,
5159 NULL,
5160 NULL,
5161 1);
5162 op->p.buffer_mode_fun = file_buffer_mode;
5163
5164 return (Scheme_Object *)op;
5165 }
5166
5167 /*========================================================================*/
5168 /* fd output ports */
5169 /*========================================================================*/
5170
5171 static int
fd_flush_done(Scheme_Object * port)5172 fd_flush_done(Scheme_Object *port)
5173 {
5174 Scheme_FD *fop;
5175 Scheme_Output_Port *op;
5176
5177 op = scheme_output_port_record(port);
5178
5179 if (op->closed) return 1;
5180
5181 fop = (Scheme_FD *)op->port_data;
5182
5183 return !fop->flushing;
5184 }
5185
wait_until_fd_flushed(Scheme_Output_Port * op,int enable_break)5186 static void wait_until_fd_flushed(Scheme_Output_Port *op, int enable_break)
5187 {
5188 scheme_block_until_enable_break(fd_flush_done, NULL, (Scheme_Object *)op,
5189 0.0, enable_break);
5190 }
5191
5192 static int
fd_write_ready(Scheme_Object * port)5193 fd_write_ready (Scheme_Object *port)
5194 {
5195 /* As always, the result of this function is only meaningful when
5196 the port has been flushed. */
5197
5198 Scheme_FD *fop;
5199 Scheme_Output_Port *op;
5200
5201 op = scheme_output_port_record(port);
5202 fop = (Scheme_FD *)op->port_data;
5203
5204 if (op->closed)
5205 return 1;
5206
5207 return rktio_poll_write_ready(scheme_rktio, fop->fd);
5208 }
5209
5210 static void
fd_write_need_wakeup(Scheme_Object * port,void * fds)5211 fd_write_need_wakeup(Scheme_Object *port, void *fds)
5212 {
5213 Scheme_Output_Port *op;
5214 Scheme_FD *fop;
5215
5216 op = scheme_output_port_record(port);
5217 fop = (Scheme_FD *)op->port_data;
5218
5219 rktio_poll_add(scheme_rktio, fop->fd, fds, RKTIO_POLL_WRITE);
5220 }
5221
release_flushing_lock(void * _fop)5222 static void release_flushing_lock(void *_fop)
5223 {
5224 Scheme_FD *fop;
5225
5226 fop = (Scheme_FD *)_fop;
5227
5228 fop->flushing = 0;
5229 }
5230
consume_buffer_bytes(Scheme_FD * fop,intptr_t wrote)5231 static void consume_buffer_bytes(Scheme_FD *fop, intptr_t wrote)
5232 {
5233 if (fop->bufcount == wrote)
5234 fop->bufcount = 0;
5235 else {
5236 memmove(fop->buffer + wrote, fop->buffer, fop->bufcount - wrote);
5237 fop->bufcount -= wrote;
5238 }
5239 }
5240
flush_fd(Scheme_Output_Port * op,const char * volatile bufstr,volatile uintptr_t buflen,volatile uintptr_t offset,int immediate_only,int enable_break)5241 static intptr_t flush_fd(Scheme_Output_Port *op,
5242 const char * volatile bufstr, volatile uintptr_t buflen, volatile uintptr_t offset,
5243 int immediate_only, int enable_break)
5244 /* immediate_only == 1 => write at least one character, then give up;
5245 immediate_only == 2 => never block */
5246 {
5247 Scheme_FD * volatile fop = (Scheme_FD *)op->port_data;
5248 volatile intptr_t wrote = 0;
5249 volatile int consume_buffer;
5250
5251 if (fop->flushing) {
5252 if (scheme_force_port_closed) {
5253 /* Give up */
5254 return 0;
5255 }
5256
5257 if (immediate_only == 2)
5258 return 0;
5259
5260 wait_until_fd_flushed(op, enable_break);
5261
5262 if (op->closed)
5263 return 0;
5264 }
5265
5266 if (!bufstr) {
5267 bufstr = (char *)fop->buffer;
5268 buflen = fop->bufcount;
5269 consume_buffer = 1;
5270 } else
5271 consume_buffer = 0;
5272
5273 if (buflen) {
5274 fop->flushing = 1;
5275
5276 while (1) {
5277 intptr_t len;
5278
5279 len = rktio_write(scheme_rktio, fop->fd, bufstr + offset, buflen - offset);
5280
5281 if (!len) {
5282 /* Need to block; remember that we're holding a lock. */
5283 Scheme_Object *sema;
5284
5285 if (immediate_only == 2) {
5286 fop->flushing = 0;
5287 if (consume_buffer)
5288 consume_buffer_bytes(fop, wrote);
5289 return wrote;
5290 }
5291
5292 sema = scheme_rktio_fd_to_semaphore(fop->fd, MZFD_CREATE_WRITE);
5293
5294 BEGIN_ESCAPEABLE(release_flushing_lock, fop);
5295 if (sema)
5296 scheme_wait_sema(sema, enable_break ? -1 : 0);
5297 else
5298 scheme_block_until_enable_break(fd_write_ready,
5299 fd_write_need_wakeup,
5300 (Scheme_Object *)op, 0.0,
5301 enable_break);
5302 END_ESCAPEABLE();
5303
5304 if (op->closed)
5305 return 0;
5306 } else if (len == RKTIO_WRITE_ERROR) {
5307 if (consume_buffer) {
5308 /* Drop unsuccessfully flushed bytes. This isn't the
5309 obviously right choice, but otherwise a future flush
5310 attempt (including one triggered by trying to close the
5311 port or one triggered by a plumber) will likely just fail
5312 again, which is probably worse than dropping bytes. */
5313 consume_buffer_bytes(fop, buflen);
5314 }
5315 if (scheme_force_port_closed) {
5316 /* Don't signal exn or wait. Just give up. */
5317 return wrote;
5318 } else {
5319 fop->flushing = 0;
5320 scheme_raise_exn(MZEXN_FAIL_FILESYSTEM,
5321 "error writing to stream port\n"
5322 " system error: %R");
5323 return 0; /* doesn't get here */
5324 }
5325 } else if ((len + offset == buflen) || immediate_only) {
5326 if (consume_buffer)
5327 consume_buffer_bytes(fop, buflen);
5328 fop->flushing = 0;
5329 return wrote + len;
5330 } else {
5331 offset += len;
5332 wrote += len;
5333 }
5334 }
5335 }
5336
5337 return wrote;
5338 }
5339
5340 static intptr_t
fd_write_string_slow(Scheme_Output_Port * port,const char * str,intptr_t d,intptr_t len,int rarely_block,int enable_break)5341 fd_write_string_slow(Scheme_Output_Port *port,
5342 const char *str, intptr_t d, intptr_t len,
5343 int rarely_block, int enable_break)
5344 {
5345 /* Note: !flush => !rarely_block, !len => flush */
5346
5347 Scheme_FD *fop;
5348 intptr_t l;
5349 int flush = (!len || rarely_block);
5350
5351 fop = (Scheme_FD *)port->port_data;
5352
5353 if (!len) {
5354 if (fop->bufcount)
5355 flush_fd(port, NULL, 0, 0, rarely_block, enable_break);
5356
5357 if (fop->bufcount)
5358 return -1;
5359 else
5360 return 0;
5361 }
5362
5363 if (!fop->bufcount && flush) {
5364 /* Nothing buffered. Write directly. */
5365 return flush_fd(port, str, d + len, d, rarely_block, enable_break);
5366 }
5367
5368 if (fop->flushing) {
5369 if (rarely_block == 2)
5370 return -1; /* -1 means 0 written && still have unflushed */
5371 wait_until_fd_flushed(port, enable_break);
5372 }
5373
5374 /* Might have been closed while we waited */
5375 if (port->closed)
5376 return 0;
5377
5378 l = MZPORT_FD_BUFFSIZE - fop->bufcount;
5379 if ((len <= l) && (!flush || !rarely_block)) {
5380 memcpy(fop->buffer + fop->bufcount, str + d, len);
5381 fop->bufcount += len;
5382 } else {
5383 if (fop->bufcount) {
5384 flush_fd(port, NULL, 0, 0, (rarely_block == 2) ? 2 : 0, enable_break);
5385 if (rarely_block && fop->bufcount)
5386 return -1; /* -1 means 0 written && still have unflushed */
5387 }
5388
5389 if (!flush && (len <= MZPORT_FD_BUFFSIZE)) {
5390 memcpy(fop->buffer, str + d, len);
5391 fop->bufcount = len;
5392 } else
5393 return flush_fd(port, str, len + d, d, rarely_block, enable_break);
5394 }
5395
5396 /* If we got this far, !rarely_block. */
5397
5398 if ((flush || (fop->flush == MZ_FLUSH_ALWAYS)) && fop->bufcount) {
5399 flush_fd(port, NULL, 0, 0, 0, enable_break);
5400 } else if (fop->flush == MZ_FLUSH_BY_LINE) {
5401 intptr_t i;
5402
5403 for (i = len; i--; ) {
5404 if (str[d] == '\n' || str[d] == '\r') {
5405 flush_fd(port, NULL, 0, 0, 0, enable_break);
5406 break;
5407 }
5408 d++;
5409 }
5410 }
5411
5412 return len;
5413 }
5414
5415 static intptr_t
fd_write_string(Scheme_Output_Port * port,const char * str,intptr_t d,intptr_t len,int rarely_block,int enable_break)5416 fd_write_string(Scheme_Output_Port *port,
5417 const char *str, intptr_t d, intptr_t len,
5418 int rarely_block, int enable_break)
5419 XFORM_ASSERT_NO_CONVERSION
5420 {
5421 Scheme_FD *fop;
5422 intptr_t l;
5423 int flush = (!len || rarely_block);
5424
5425 fop = (Scheme_FD *)port->port_data;
5426
5427 if (!flush && !fop->flushing && (fop->flush == MZ_FLUSH_NEVER)) {
5428 l = MZPORT_FD_BUFFSIZE - fop->bufcount;
5429 if (len <= l) {
5430 memcpy(fop->buffer + fop->bufcount, str + d, len);
5431 fop->bufcount += len;
5432 return len;
5433 }
5434 }
5435
5436 return fd_write_string_slow(port, str, d, len, rarely_block, enable_break);
5437 }
5438
end_fd_flush_done(Scheme_Object * fop)5439 static int end_fd_flush_done(Scheme_Object *fop)
5440 {
5441 return rktio_poll_write_flushed(scheme_rktio, ((Scheme_FD *)fop)->fd);
5442 }
5443
end_fd_flush_needs_wakeup(Scheme_Object * fop,void * fds)5444 static void end_fd_flush_needs_wakeup(Scheme_Object *fop, void *fds)
5445 {
5446 rktio_poll_add(scheme_rktio, ((Scheme_FD *)fop)->fd, fds, RKTIO_POLL_FLUSH);
5447 }
5448
5449 static void
fd_close_output(Scheme_Output_Port * port)5450 fd_close_output(Scheme_Output_Port *port)
5451 {
5452 Scheme_FD *fop = (Scheme_FD *)port->port_data;
5453 int rc;
5454
5455 if (fop->bufcount)
5456 flush_fd(port, NULL, 0, 0, 0, 0);
5457
5458 if (fop->flushing && fop->bufcount && !scheme_force_port_closed) {
5459 wait_until_fd_flushed(port, 0);
5460 if (port->closed)
5461 return;
5462 }
5463
5464 if (!scheme_force_port_closed && fop->fd) {
5465 /* Check for flushing at the rktio level (not to be confused
5466 with plumber flushes): */
5467 while (!rktio_poll_write_flushed(scheme_rktio, fop->fd)) {
5468 scheme_block_until(end_fd_flush_done, end_fd_flush_needs_wakeup, (Scheme_Object *)fop, 0.0);
5469 }
5470 }
5471
5472 scheme_remove_flush(fop->flush_handle);
5473
5474 /* Make sure no close happened while we blocked above! */
5475 if (port->closed)
5476 return;
5477
5478 rc = adj_refcount(fop->refcount, -1);
5479 if (fop->fd) {
5480 if (!rc) {
5481 (void)scheme_rktio_fd_to_semaphore(fop->fd, MZFD_REMOVE);
5482 rktio_close(scheme_rktio, fop->fd);
5483 } else
5484 rktio_forget(scheme_rktio, fop->fd);
5485 }
5486 }
5487
5488 static void
fd_init_close_output(Scheme_Output_Port * port)5489 fd_init_close_output(Scheme_Output_Port *port)
5490 {
5491 /* never actually opened */
5492 }
5493
fd_output_buffer_mode(Scheme_Port * p,int mode)5494 static int fd_output_buffer_mode(Scheme_Port *p, int mode)
5495 {
5496 Scheme_FD *fd;
5497 Scheme_Output_Port *op = (Scheme_Output_Port *)p;
5498
5499 fd = (Scheme_FD *)op->port_data;
5500
5501 if (mode < 0) {
5502 return fd->flush;
5503 } else {
5504 int go;
5505 go = (mode > fd->flush);
5506 fd->flush = mode;
5507 if (go)
5508 flush_fd(op, NULL, 0, 0, 0, 0);
5509 return mode;
5510 }
5511 }
5512
5513 static Scheme_Object *
make_fd_output_port(rktio_fd_t * fd,Scheme_Object * name,int and_read,int flush_mode,int * refcount)5514 make_fd_output_port(rktio_fd_t *fd, Scheme_Object *name, int and_read, int flush_mode, int *refcount)
5515 {
5516 Scheme_FD *fop;
5517 char *bfr;
5518 Scheme_Object *the_port, *fh;
5519 int start_closed = 0;
5520
5521 fop = MALLOC_ONE_RT(Scheme_FD);
5522 #ifdef MZTAG_REQUIRED
5523 fop->type = scheme_rt_input_fd;
5524 #endif
5525
5526 bfr = (char *)scheme_malloc_atomic(MZPORT_FD_BUFFSIZE);
5527 fop->buffer = bfr;
5528
5529 fop->fd = fd;
5530 fop->bufcount = 0;
5531
5532 if (flush_mode > -1) {
5533 fop->flush = flush_mode;
5534 } else if (rktio_fd_is_terminal(scheme_rktio, fd)) {
5535 /* Line-buffering for terminal: */
5536 fop->flush = MZ_FLUSH_BY_LINE;
5537 } else {
5538 /* Block-buffering for everything else: */
5539 fop->flush = MZ_FLUSH_NEVER;
5540 }
5541
5542 if (refcount) {
5543 fop->refcount = refcount;
5544 if (!adj_refcount(refcount, 1)) {
5545 /* fd is already closed! */
5546 start_closed = 1;
5547 }
5548 }
5549
5550 the_port = (Scheme_Object *)scheme_make_output_port(fd_output_port_type,
5551 fop,
5552 name,
5553 scheme_write_evt_via_write,
5554 fd_write_string,
5555 (Scheme_Out_Ready_Fun)fd_write_ready,
5556 (start_closed
5557 ? fd_init_close_output
5558 : fd_close_output),
5559 (Scheme_Need_Wakeup_Output_Fun)fd_write_need_wakeup,
5560 NULL,
5561 NULL,
5562 1);
5563 ((Scheme_Port *)the_port)->buffer_mode_fun = fd_output_buffer_mode;
5564
5565 fh = scheme_add_flush(NULL, the_port, 0);
5566 fop->flush_handle = fh;
5567
5568 if (start_closed)
5569 scheme_close_output_port(the_port);
5570
5571 if (and_read) {
5572 int *rc;
5573 Scheme_Object *a[2];
5574 rc = malloc_refcount(1, 1);
5575 fop->refcount = rc;
5576 fd = rktio_system_fd(scheme_rktio, rktio_fd_system_fd(scheme_rktio, fd), rktio_fd_modes(scheme_rktio, fd));
5577 a[1] = the_port;
5578 a[0] = make_fd_input_port(fd, name, rc, 0);
5579 return scheme_values(2, a);
5580 } else
5581 return the_port;
5582 }
5583
flush_if_output_fds(Scheme_Object * o,Scheme_Close_Custodian_Client * f,void * data)5584 static void flush_if_output_fds(Scheme_Object *o, Scheme_Close_Custodian_Client *f, void *data)
5585 {
5586 if (SCHEME_OUTPORTP(o)) {
5587 scheme_flush_if_output_fds(o);
5588 }
5589 }
5590
scheme_flush_if_output_fds(Scheme_Object * o)5591 void scheme_flush_if_output_fds(Scheme_Object *o)
5592 {
5593 Scheme_Output_Port *op;
5594 op = scheme_output_port_record(o);
5595 if (SAME_OBJ(op->sub_type, fd_output_port_type))
5596 scheme_flush_output(o);
5597 }
5598
5599 Scheme_Object *
scheme_make_fd_output_port(int fd,Scheme_Object * name,int regfile,int textmode,int read_too)5600 scheme_make_fd_output_port(int fd, Scheme_Object *name, int regfile, int textmode, int read_too)
5601 {
5602 rktio_fd_t *rfd;
5603
5604 rfd = rktio_system_fd(scheme_rktio,
5605 fd,
5606 (RKTIO_OPEN_WRITE
5607 | (regfile ? RKTIO_OPEN_REGFILE : RKTIO_OPEN_NOT_REGFILE)
5608 | (read_too ? RKTIO_OPEN_READ : 0)
5609 | (textmode ? RKTIO_OPEN_TEXT : 0)));
5610
5611 return make_fd_output_port(rfd, name, read_too, -1, NULL);
5612 }
5613
5614 Scheme_Object *
scheme_make_rktio_fd_output_port(rktio_fd_t * rfd,Scheme_Object * name,int read_too)5615 scheme_make_rktio_fd_output_port(rktio_fd_t *rfd, Scheme_Object *name, int read_too)
5616 {
5617 return make_fd_output_port(rfd, name, read_too, -1, NULL);
5618 }
5619
scheme_rktio_write_all(struct rktio_fd_t * fd,const char * data,intptr_t len)5620 void scheme_rktio_write_all(struct rktio_fd_t *fd, const char *data, intptr_t len)
5621 {
5622 while (len > 0) {
5623 intptr_t r;
5624 r = rktio_write(scheme_rktio, fd, data, len);
5625 if (r == RKTIO_WRITE_ERROR)
5626 break;
5627 len -= r;
5628 }
5629 }
5630
5631 /*========================================================================*/
5632 /* null output ports */
5633 /*========================================================================*/
5634
5635 static intptr_t
null_write_bytes(Scheme_Output_Port * port,const char * str,intptr_t d,intptr_t len,int rarely_block,int enable_break)5636 null_write_bytes(Scheme_Output_Port *port,
5637 const char *str, intptr_t d, intptr_t len,
5638 int rarely_block, int enable_break)
5639 {
5640 return len;
5641 }
5642
5643 static void
null_close_out(Scheme_Output_Port * port)5644 null_close_out (Scheme_Output_Port *port)
5645 {
5646 }
5647
5648 static Scheme_Object *
null_write_evt(Scheme_Output_Port * op,const char * str,intptr_t offset,intptr_t size)5649 null_write_evt(Scheme_Output_Port *op, const char *str, intptr_t offset, intptr_t size)
5650 {
5651 Scheme_Object *a[2];
5652 a[0] = scheme_always_ready_evt;
5653 a[1] = scheme_make_closed_prim(return_data, scheme_make_integer(size));
5654 return scheme_wrap_evt(2, a);
5655 }
5656
5657 static Scheme_Object *
null_write_special_evt(Scheme_Output_Port * op,Scheme_Object * v)5658 null_write_special_evt(Scheme_Output_Port *op, Scheme_Object *v)
5659 {
5660 Scheme_Object *a[2];
5661 a[0] = scheme_always_ready_evt;
5662 a[1] = scheme_make_closed_prim(return_data, scheme_true);
5663 return scheme_wrap_evt(2, a);
5664 }
5665
5666 static int
null_write_special(Scheme_Output_Port * op,Scheme_Object * v,int nonblock)5667 null_write_special(Scheme_Output_Port *op, Scheme_Object *v, int nonblock)
5668 {
5669 return 1;
5670 }
5671
5672 Scheme_Object *
scheme_make_null_output_port(int can_write_special)5673 scheme_make_null_output_port(int can_write_special)
5674 {
5675 Scheme_Output_Port *op;
5676
5677 op = scheme_make_output_port(scheme_null_output_port_type,
5678 NULL,
5679 scheme_intern_symbol("null"),
5680 null_write_evt,
5681 null_write_bytes,
5682 NULL,
5683 null_close_out,
5684 NULL,
5685 (can_write_special
5686 ? null_write_special_evt
5687 : NULL),
5688 (can_write_special
5689 ? null_write_special
5690 : NULL),
5691 0);
5692
5693 return (Scheme_Object *)op;
5694 }
5695
5696 /*========================================================================*/
5697 /* redirect output ports */
5698 /*========================================================================*/
5699
5700 static Scheme_Object *redirect_write_bytes_k(void);
5701
5702 intptr_t
scheme_redirect_write_bytes(Scheme_Output_Port * op,const char * str,intptr_t d,intptr_t len,int rarely_block,int enable_break)5703 scheme_redirect_write_bytes(Scheme_Output_Port *op,
5704 const char *str, intptr_t d, intptr_t len,
5705 int rarely_block, int enable_break)
5706 {
5707 /* arbitrary nesting means we can overflow the stack */
5708 #ifdef DO_STACK_CHECK
5709 # include "mzstkchk.h"
5710 {
5711 Scheme_Thread *p = scheme_current_thread;
5712 Scheme_Object *n;
5713
5714 p->ku.k.p1 = (void *)op;
5715 p->ku.k.p2 = (void *)str;
5716 p->ku.k.i1 = d;
5717 p->ku.k.i2 = len;
5718 p->ku.k.i3 = rarely_block;
5719 p->ku.k.i4 = enable_break;
5720
5721 n = scheme_handle_stack_overflow(redirect_write_bytes_k);
5722 return SCHEME_INT_VAL(n);
5723 }
5724 #endif
5725
5726 return scheme_put_byte_string("redirect-output",
5727 (Scheme_Object *)op,
5728 str, d, len,
5729 (enable_break && !rarely_block) ? -1 : rarely_block);
5730 }
5731
5732 static intptr_t
redirect_write_bytes(Scheme_Output_Port * op,const char * str,intptr_t d,intptr_t len,int rarely_block,int enable_break)5733 redirect_write_bytes(Scheme_Output_Port *op,
5734 const char *str, intptr_t d, intptr_t len,
5735 int rarely_block, int enable_break)
5736 {
5737 return scheme_redirect_write_bytes(scheme_output_port_record((Scheme_Object *)op->port_data),
5738 str, d, len,
5739 rarely_block, enable_break);
5740 }
5741
redirect_write_bytes_k(void)5742 static Scheme_Object *redirect_write_bytes_k(void)
5743 {
5744 Scheme_Thread *p = scheme_current_thread;
5745 Scheme_Output_Port *op = (Scheme_Output_Port *)p->ku.k.p1;
5746 const char *str = (const char *)p->ku.k.p2;
5747 intptr_t d = p->ku.k.i1;
5748 intptr_t len = p->ku.k.i2;
5749 int rarely_block = p->ku.k.i3;
5750 int enable_break = p->ku.k.i4;
5751 intptr_t n;
5752
5753 p->ku.k.p1 = NULL;
5754 p->ku.k.p2 = NULL;
5755
5756 n = scheme_redirect_write_bytes(op, str, d, len, rarely_block, enable_break);
5757
5758 return scheme_make_integer(n);
5759 }
5760
5761 static Scheme_Object *redirect_write_special_k(void);
5762
scheme_redirect_write_special(Scheme_Output_Port * op,Scheme_Object * v,int nonblock)5763 int scheme_redirect_write_special (Scheme_Output_Port *op, Scheme_Object *v, int nonblock)
5764 {
5765 Scheme_Object *a[2];
5766
5767 #ifdef DO_STACK_CHECK
5768 {
5769 # include "mzstkchk.h"
5770 {
5771 Scheme_Thread *p = scheme_current_thread;
5772 Scheme_Object *n;
5773
5774 p->ku.k.p1 = (void *)op;
5775 p->ku.k.p2 = (void *)v;
5776 p->ku.k.i1 = nonblock;
5777
5778 n = scheme_handle_stack_overflow(redirect_write_special_k);
5779 return SCHEME_INT_VAL(n);
5780 }
5781 }
5782 #endif
5783
5784 a[0] = (Scheme_Object *)v;
5785 a[1] = (Scheme_Object *)op;
5786
5787 if (nonblock)
5788 v = scheme_write_special_nonblock(2, a);
5789 else
5790 v = scheme_write_special(2, a);
5791
5792 return SCHEME_TRUEP(v);
5793 }
5794
redirect_write_special_k(void)5795 static Scheme_Object *redirect_write_special_k(void)
5796 {
5797 Scheme_Thread *p = scheme_current_thread;
5798 Scheme_Output_Port *op = (Scheme_Output_Port *)p->ku.k.p1;
5799 Scheme_Object *v = (Scheme_Object *)p->ku.k.p2;
5800 intptr_t nonblock = p->ku.k.i1;
5801 intptr_t n;
5802
5803 p->ku.k.p1 = NULL;
5804 p->ku.k.p2 = NULL;
5805
5806 n = scheme_redirect_write_special(op, v, nonblock);
5807
5808 return scheme_make_integer(n);
5809 }
5810
5811 static void
redirect_close_out(Scheme_Output_Port * port)5812 redirect_close_out (Scheme_Output_Port *port)
5813 {
5814 }
5815
5816 static Scheme_Object *
redirect_write_evt(Scheme_Output_Port * op,const char * str,intptr_t offset,intptr_t size)5817 redirect_write_evt(Scheme_Output_Port *op, const char *str, intptr_t offset, intptr_t size)
5818 {
5819 return scheme_make_write_evt("redirect-write-evt",
5820 (Scheme_Object *)op->port_data,
5821 NULL, (char *)str, offset, size);
5822 }
5823
5824 static Scheme_Object *
redirect_write_special_evt(Scheme_Output_Port * op,Scheme_Object * special)5825 redirect_write_special_evt(Scheme_Output_Port *op, Scheme_Object *special)
5826 {
5827 return scheme_make_write_evt("redirect-write-evt",
5828 (Scheme_Object *)op->port_data,
5829 special, NULL, 0, 0);
5830 }
5831
5832 static int
redirect_write_special(Scheme_Output_Port * op,Scheme_Object * special,int nonblock)5833 redirect_write_special(Scheme_Output_Port *op, Scheme_Object *special, int nonblock)
5834 {
5835 return scheme_redirect_write_special(scheme_output_port_record((Scheme_Object *)op->port_data),
5836 special,
5837 nonblock);
5838 }
5839
5840 Scheme_Object *
scheme_make_redirect_output_port(Scheme_Object * port)5841 scheme_make_redirect_output_port(Scheme_Object *port)
5842 {
5843 Scheme_Output_Port *op;
5844 int can_write_special;
5845
5846 op = scheme_output_port_record(port);
5847 can_write_special = !!op->write_special_fun;
5848
5849 op = scheme_make_output_port(scheme_redirect_output_port_type,
5850 port,
5851 scheme_intern_symbol("redirect"),
5852 redirect_write_evt,
5853 redirect_write_bytes,
5854 NULL,
5855 redirect_close_out,
5856 NULL,
5857 (can_write_special
5858 ? redirect_write_special_evt
5859 : NULL),
5860 (can_write_special
5861 ? redirect_write_special
5862 : NULL),
5863 0);
5864
5865 return (Scheme_Object *)op;
5866 }
5867
5868 static Scheme_Object *redirect_get_or_peek_bytes_k(void);
5869
scheme_redirect_get_or_peek_bytes(Scheme_Input_Port * orig_port,Scheme_Input_Port * port,char * buffer,intptr_t offset,intptr_t size,int nonblock,int peek,Scheme_Object * peek_skip,Scheme_Object * unless,Scheme_Schedule_Info * sinfo)5870 intptr_t scheme_redirect_get_or_peek_bytes(Scheme_Input_Port *orig_port,
5871 Scheme_Input_Port *port,
5872 char *buffer, intptr_t offset, intptr_t size,
5873 int nonblock,
5874 int peek, Scheme_Object *peek_skip,
5875 Scheme_Object *unless,
5876 Scheme_Schedule_Info *sinfo)
5877 {
5878 int r;
5879
5880 if (sinfo) {
5881 scheme_set_sync_target(sinfo, (Scheme_Object *)port, (Scheme_Object *)orig_port, NULL, 0, 1, NULL);
5882 return 0;
5883 }
5884
5885 #ifdef DO_STACK_CHECK
5886 {
5887 # include "mzstkchk.h"
5888 {
5889 Scheme_Thread *p = scheme_current_thread;
5890 Scheme_Object *n;
5891
5892 p->ku.k.p1 = (void *)port;
5893 p->ku.k.p2 = (void *)buffer;
5894 p->ku.k.p3 = (void *)peek_skip;
5895 p->ku.k.p4 = (void *)unless;
5896 p->ku.k.p4 = (void *)orig_port;
5897 p->ku.k.i1 = offset;
5898 p->ku.k.i1 = size;
5899 p->ku.k.i2 = nonblock;
5900 p->ku.k.i3 = peek;
5901
5902 n = scheme_handle_stack_overflow(redirect_get_or_peek_bytes_k);
5903 return SCHEME_INT_VAL(n);
5904 }
5905 }
5906 #endif
5907
5908 r = scheme_get_byte_string_special_ok_unless("redirect-read-or-peek",
5909 (Scheme_Object *)port,
5910 buffer, offset, size,
5911 ((nonblock == -1)
5912 ? -1
5913 : (nonblock ? 2 : 1)),
5914 peek, (peek ? peek_skip : NULL),
5915 unless);
5916
5917 if (r == SCHEME_SPECIAL) {
5918 Scheme_Object *res;
5919 res = scheme_get_special_proc((Scheme_Object *)port);
5920 orig_port->special = res;
5921 }
5922
5923 return r;
5924 }
5925
redirect_get_or_peek_bytes_k(void)5926 static Scheme_Object *redirect_get_or_peek_bytes_k(void)
5927 {
5928 Scheme_Thread *p = scheme_current_thread;
5929 Scheme_Input_Port *ip = (Scheme_Input_Port *)p->ku.k.p1;
5930 char *buffer = (char *)p->ku.k.p2;
5931 Scheme_Object *peek_skip = (Scheme_Object *)p->ku.k.p3;
5932 Scheme_Object *unless = (Scheme_Object *)p->ku.k.p4;
5933 Scheme_Input_Port *orig_port = (Scheme_Input_Port *)p->ku.k.p5;
5934 intptr_t d = p->ku.k.i1;
5935 intptr_t len = p->ku.k.i2;
5936 int nonblock = p->ku.k.i3;
5937 int peek = p->ku.k.i4;
5938 intptr_t n;
5939
5940 p->ku.k.p1 = NULL;
5941 p->ku.k.p2 = NULL;
5942 p->ku.k.p3 = NULL;
5943 p->ku.k.p4 = NULL;
5944 p->ku.k.p5 = NULL;
5945
5946 n = scheme_redirect_get_or_peek_bytes(orig_port, ip, buffer, d, len,
5947 nonblock,
5948 peek, peek_skip,
5949 unless, NULL);
5950
5951 return scheme_make_integer(n);
5952 }
5953
5954 /*========================================================================*/
5955 /* subprocess */
5956 /*========================================================================*/
5957
5958 #define SCHEME_SUBPROCESSP(o) SAME_TYPE(SCHEME_TYPE(o), scheme_subprocess_type)
5959
close_subprocess_handle(void * so,void * ignored)5960 static void close_subprocess_handle(void *so, void *ignored)
5961 {
5962 Scheme_Subprocess *sp = (Scheme_Subprocess *)so;
5963
5964 if (sp->proc) {
5965 rktio_process_forget(scheme_rktio, sp->proc);
5966 sp->proc = NULL;
5967 }
5968 }
5969
child_mref_done(Scheme_Subprocess * sp)5970 static void child_mref_done(Scheme_Subprocess *sp)
5971 {
5972 if (sp->mref) {
5973 scheme_remove_managed(sp->mref, (Scheme_Object *)sp);
5974 sp->mref = NULL;
5975 }
5976 }
5977
subp_done(Scheme_Object * so)5978 static int subp_done(Scheme_Object *so)
5979 {
5980 Scheme_Subprocess *sp = (Scheme_Subprocess*)so;
5981 int done;
5982
5983 if (!sp->proc) return 1;
5984
5985 done = rktio_poll_process_done(scheme_rktio, sp->proc);
5986
5987 if (done)
5988 child_mref_done(sp);
5989
5990 return done;
5991 }
5992
subp_needs_wakeup(Scheme_Object * so,void * fds)5993 static void subp_needs_wakeup(Scheme_Object *so, void *fds)
5994 {
5995 Scheme_Subprocess *sp = (Scheme_Subprocess*)so;
5996
5997 if (sp->proc)
5998 rktio_poll_add_process(scheme_rktio, sp->proc, fds);
5999 }
6000
subprocess_status(int argc,Scheme_Object ** argv)6001 static Scheme_Object *subprocess_status(int argc, Scheme_Object **argv)
6002 {
6003 Scheme_Subprocess *sp = (Scheme_Subprocess *)argv[0];
6004 rktio_status_t *st;
6005
6006 if (!SAME_TYPE(SCHEME_TYPE(argv[0]), scheme_subprocess_type))
6007 scheme_wrong_contract("subprocess-status", "subprocess?", 0, argc, argv);
6008
6009 st = rktio_process_status(scheme_rktio, sp->proc);
6010
6011 if (!st) {
6012 scheme_raise_exn(MZEXN_FAIL,
6013 "subprocess-status: error getting status\n"
6014 " system error: %R");
6015 }
6016
6017 if (st->running) {
6018 free(st);
6019 return scheme_intern_symbol("running");
6020 } else {
6021 int status = st->result;
6022 free(st);
6023 child_mref_done(sp);
6024 return scheme_make_integer_value(status);
6025 }
6026 }
6027
6028
register_subprocess_wait()6029 static void register_subprocess_wait()
6030 {
6031 scheme_add_evt(scheme_subprocess_type, subp_done,
6032 subp_needs_wakeup, NULL, 0);
6033 }
6034
subprocess_wait(int argc,Scheme_Object ** argv)6035 static Scheme_Object *subprocess_wait(int argc, Scheme_Object **argv)
6036 {
6037 if (!SAME_TYPE(SCHEME_TYPE(argv[0]), scheme_subprocess_type))
6038 scheme_wrong_contract("subprocess-wait", "subprocess?", 0, argc, argv);
6039
6040 {
6041 Scheme_Subprocess *sp = (Scheme_Subprocess *)argv[0];
6042
6043 scheme_block_until(subp_done, subp_needs_wakeup, (Scheme_Object *)sp, (float)0.0);
6044
6045 return scheme_void;
6046 }
6047 }
6048
do_subprocess_kill(Scheme_Object * _sp,Scheme_Object * killp,int can_error)6049 static Scheme_Object *do_subprocess_kill(Scheme_Object *_sp, Scheme_Object *killp, int can_error)
6050 {
6051 Scheme_Subprocess *sp = (Scheme_Subprocess *)_sp;
6052 int ok;
6053
6054 if (!sp->proc)
6055 return scheme_void;
6056
6057 if (SCHEME_TRUEP(killp))
6058 ok = rktio_process_kill(scheme_rktio, sp->proc);
6059 else
6060 ok = rktio_process_interrupt(scheme_rktio, sp->proc);
6061
6062 if (!ok) {
6063 if (can_error)
6064 scheme_raise_exn(MZEXN_FAIL,
6065 "subprocess-kill: operation failed\n"
6066 " system error: %R");
6067 }
6068
6069 return scheme_void;
6070 }
6071
kill_subproc(Scheme_Object * o,void * data)6072 static void kill_subproc(Scheme_Object *o, void *data)
6073 {
6074 (void)do_subprocess_kill(o, scheme_true, 0);
6075 }
6076
interrupt_subproc(Scheme_Object * o,void * data)6077 static void interrupt_subproc(Scheme_Object *o, void *data)
6078 {
6079 (void)do_subprocess_kill(o, scheme_false, 0);
6080 }
6081
subprocess_kill(int argc,Scheme_Object ** argv)6082 static Scheme_Object *subprocess_kill(int argc, Scheme_Object **argv)
6083 {
6084 if (!SAME_TYPE(SCHEME_TYPE(argv[0]), scheme_subprocess_type))
6085 scheme_wrong_contract("subprocess-kill", "subprocess?", 0, argc, argv);
6086
6087 return do_subprocess_kill(argv[0], argv[1], 1);
6088 }
6089
subprocess_pid(int argc,Scheme_Object ** argv)6090 static Scheme_Object *subprocess_pid(int argc, Scheme_Object **argv)
6091 {
6092 intptr_t pid;
6093
6094 if (!SAME_TYPE(SCHEME_TYPE(argv[0]), scheme_subprocess_type))
6095 scheme_wrong_contract("subprocess-pid", "subprocess?", 0, argc, argv);
6096
6097 pid = rktio_process_pid(scheme_rktio, ((Scheme_Subprocess *)argv[0])->proc);
6098
6099 return scheme_make_integer_value(pid);
6100 }
6101
subprocess_p(int argc,Scheme_Object ** argv)6102 static Scheme_Object *subprocess_p(int argc, Scheme_Object **argv)
6103 {
6104 return (SAME_TYPE(SCHEME_TYPE(argv[0]), scheme_subprocess_type)
6105 ? scheme_true
6106 : scheme_false);
6107 }
6108
subproc_cust_mode_p(int argc,Scheme_Object ** argv)6109 static Scheme_Object *subproc_cust_mode_p(int argc, Scheme_Object **argv)
6110 {
6111 if (SCHEME_FALSEP(argv[0]))
6112 return argv[0];
6113 if (SCHEME_SYMBOLP(argv[0]) && !SCHEME_SYM_WEIRDP(argv[0])) {
6114 if (!strcmp(SCHEME_SYM_VAL(argv[0]), "kill")
6115 || !strcmp(SCHEME_SYM_VAL(argv[0]), "interrupt"))
6116 return argv[0];
6117 }
6118
6119 return NULL;
6120 }
6121
current_subproc_cust_mode(int argc,Scheme_Object * argv[])6122 static Scheme_Object *current_subproc_cust_mode (int argc, Scheme_Object *argv[])
6123 {
6124 return scheme_param_config2("current-subprocess-custodian-mode", scheme_make_integer(MZCONFIG_SUBPROC_CUSTODIAN_MODE),
6125 argc, argv,
6126 -1, subproc_cust_mode_p, "(or/c 'interrupt 'kill #f)", 1);
6127 }
6128
subproc_group_on(int argc,Scheme_Object * argv[])6129 static Scheme_Object *subproc_group_on (int argc, Scheme_Object *argv[])
6130 {
6131 return scheme_param_config("subprocess-group-enabled", scheme_make_integer(MZCONFIG_SUBPROC_GROUP_ENABLED),
6132 argc, argv,
6133 -1, NULL, NULL, 1);
6134 }
6135
subprocess(int c,Scheme_Object * args[])6136 static Scheme_Object *subprocess(int c, Scheme_Object *args[])
6137 /* subprocess(out, in, err, exe, arg ...) */
6138 {
6139 const char *name = "subprocess";
6140 Scheme_Object *inport;
6141 Scheme_Object *outport;
6142 Scheme_Object *errport;
6143 Scheme_Object *a[4];
6144 Scheme_Subprocess *subproc;
6145 Scheme_Object *cust_mode, *current_dir, *group;
6146 int flags = 0;
6147 rktio_fd_t *stdout_fd = NULL;
6148 rktio_fd_t *stdin_fd = NULL;
6149 rktio_fd_t *stderr_fd = NULL;
6150 int need_forget_out = 0, need_forget_in = 0, need_forget_err = 0;
6151 rktio_envvars_t *envvars;
6152 rktio_process_result_t *result;
6153 Scheme_Config *config;
6154 int command_arg_i;
6155 int argc;
6156 char **argv, *command;
6157
6158 /*--------------------------------------------*/
6159 /* Sort out ports (create later if necessary) */
6160 /*--------------------------------------------*/
6161
6162 if (SCHEME_TRUEP(args[0])) {
6163 outport = args[0];
6164 if (SCHEME_OUTPUT_PORTP(outport) && SCHEME_TRUEP(scheme_file_stream_port_p(1, &outport))) {
6165 Scheme_Output_Port *op;
6166
6167 op = scheme_output_port_record(outport);
6168
6169 CHECK_PORT_CLOSED(name, "output", port, op->closed);
6170
6171 if (SAME_OBJ(op->sub_type, file_output_port_type)) {
6172 int tmp;
6173 tmp = MSC_IZE(fileno)(((Scheme_Output_File *)op->port_data)->f);
6174 stdout_fd = rktio_system_fd(scheme_rktio, tmp, RKTIO_OPEN_WRITE | RKTIO_OPEN_NOT_REGFILE);
6175 need_forget_out = 1;
6176 } else if (SAME_OBJ(op->sub_type, fd_output_port_type))
6177 stdout_fd = ((Scheme_FD *)op->port_data)->fd;
6178 } else
6179 scheme_wrong_contract(name, "(or/c (and/c file-stream-port? output-port?) #f)", 0, c, args);
6180 }
6181
6182 if (SCHEME_TRUEP(args[1])) {
6183 inport = args[1];
6184 if (SCHEME_INPUT_PORTP(inport) && SCHEME_TRUEP(scheme_file_stream_port_p(1, &inport))) {
6185 Scheme_Input_Port *ip;
6186
6187 ip = scheme_input_port_record(inport);
6188
6189 CHECK_PORT_CLOSED(name, "input", port, ip->closed);
6190
6191 if (SAME_OBJ(ip->sub_type, file_input_port_type)) {
6192 int tmp;
6193 tmp = MSC_IZE(fileno)(((Scheme_Input_File *)ip->port_data)->f);
6194 stdin_fd = rktio_system_fd(scheme_rktio, tmp, RKTIO_OPEN_READ | RKTIO_OPEN_NOT_REGFILE);
6195 need_forget_in = 1;
6196 } else if (SAME_OBJ(ip->sub_type, fd_input_port_type))
6197 stdin_fd = ((Scheme_FD *)ip->port_data)->fd;
6198 } else
6199 scheme_wrong_contract(name, "(or/c (and/c file-stream-port? input-port?) #f)", 1, c, args);
6200 }
6201
6202 if (SCHEME_SYMBOLP(args[2]) && !SCHEME_SYM_WEIRDP(args[2])
6203 && !strcmp("stdout", SCHEME_SYM_VAL(args[2]))) {
6204 flags |= RKTIO_PROCESS_STDOUT_AS_STDERR;
6205 } else if (SCHEME_TRUEP(args[2])) {
6206 errport = args[2];
6207 if (SCHEME_OUTPUT_PORTP(errport) && SCHEME_TRUEP(scheme_file_stream_port_p(1, &errport))) {
6208 Scheme_Output_Port *op;
6209
6210 op = scheme_output_port_record(errport);
6211
6212 CHECK_PORT_CLOSED(name, "output", port, op->closed);
6213
6214 if (SAME_OBJ(op->sub_type, file_output_port_type)) {
6215 int tmp;
6216 tmp = MSC_IZE(fileno)(((Scheme_Output_File *)op->port_data)->f);
6217 stderr_fd = rktio_system_fd(scheme_rktio, tmp, RKTIO_OPEN_WRITE | RKTIO_OPEN_NOT_REGFILE);
6218 need_forget_err = 1;
6219 } else if (SAME_OBJ(op->sub_type, fd_output_port_type))
6220 stderr_fd = ((Scheme_FD *)op->port_data)->fd;
6221 } else
6222 scheme_wrong_contract(name, "(or/c (and/c file-stream-port? output-port?) #f 'stdout)", 2, c, args);
6223 }
6224
6225 if ((c > 4)
6226 && (SCHEME_FALSEP(args[3])
6227 || SAME_OBJ(args[3], new_symbol)
6228 || SCHEME_SUBPROCESSP(args[3]))) {
6229 /* optional group specification provided */
6230 command_arg_i = 4;
6231 group = args[3];
6232 } else {
6233 command_arg_i = 3;
6234 group = scheme_false;
6235 }
6236
6237 if (!SCHEME_PATH_STRINGP(args[command_arg_i]))
6238 scheme_wrong_contract(name,
6239 (((command_arg_i == 3) && (c > 4))
6240 ? "(or/c path-string? #f 'new subprocess?)"
6241 : "path-string?"),
6242 command_arg_i, c, args);
6243
6244 /*--------------------------------------*/
6245 /* Sort out arguments */
6246 /*--------------------------------------*/
6247
6248 argc = c - command_arg_i;
6249 argv = MALLOC_N(char *, argc);
6250 {
6251 char *ef;
6252 ef = scheme_expand_string_filename(args[command_arg_i],
6253 (char *)name,
6254 NULL,
6255 SCHEME_GUARD_FILE_EXECUTE);
6256 argv[0] = ef;
6257 }
6258 {
6259 /* This is for Windows: */
6260 char *np;
6261 int nplen;
6262 nplen = strlen(argv[0]);
6263 np = scheme_normal_path_seps(argv[0], &nplen, 0);
6264 argv[0] = np;
6265 }
6266
6267 if ((c == (command_arg_i + 3)) && SAME_OBJ(args[command_arg_i+1], exact_symbol)) {
6268 argv[2] = NULL;
6269 if (!SCHEME_CHAR_STRINGP(args[command_arg_i+2]) || scheme_any_string_has_null(args[command_arg_i+2]))
6270 scheme_wrong_contract(name, CHAR_STRING_W_NO_NULLS, command_arg_i+2, c, args);
6271 {
6272 Scheme_Object *bs;
6273 bs = scheme_char_string_to_byte_string(args[command_arg_i+2]);
6274 argv[1] = SCHEME_BYTE_STR_VAL(bs);
6275 }
6276
6277 if (rktio_process_allowed_flags(scheme_rktio) & RKTIO_PROCESS_WINDOWS_EXACT_CMDLINE)
6278 flags |= RKTIO_PROCESS_WINDOWS_EXACT_CMDLINE;
6279 else
6280 scheme_contract_error(name,
6281 "exact command line not supported on this platform",
6282 "exact command", 1, args[command_arg_i + 2],
6283 NULL);
6284 } else {
6285 int i;
6286 for (i = command_arg_i + 1; i < c; i++) {
6287 if (((!SCHEME_CHAR_STRINGP(args[i]) && !SCHEME_BYTE_STRINGP(args[i]))
6288 || scheme_any_string_has_null(args[i]))
6289 && !SCHEME_PATHP(args[i]))
6290 scheme_wrong_contract(name,
6291 "(or/c path? string-no-nuls? bytes-no-nuls?)",
6292 i, c, args);
6293 {
6294 Scheme_Object *bs;
6295 bs = args[i];
6296 if (SCHEME_CHAR_STRINGP(args[i]))
6297 bs = scheme_char_string_to_byte_string_locale(bs);
6298 argv[i - command_arg_i] = SCHEME_BYTE_STR_VAL(bs);
6299 }
6300 }
6301 }
6302
6303 command = argv[0];
6304
6305 if (SCHEME_SUBPROCESSP(group)) {
6306 if (!((Scheme_Subprocess *)group)->is_group_rep) {
6307 scheme_contract_error(name, "subprocess does not represent a new group",
6308 "subprocess", 1, group,
6309 NULL);
6310 return NULL;
6311 }
6312 }
6313
6314 if (!stdin_fd || !stdout_fd || !stderr_fd)
6315 scheme_custodian_check_available(NULL, name, "file-stream");
6316
6317 /* In case `stdout_fd` or `stderr_fd` is a fifo with no read end
6318 open, wait for it. */
6319 if (stdout_fd && rktio_fd_is_pending_open(scheme_rktio, stdout_fd)) {
6320 a[0] = args[0];
6321 scheme_sync(1, a);
6322 }
6323 if (stderr_fd && rktio_fd_is_pending_open(scheme_rktio, stderr_fd)) {
6324 a[0] = args[2];
6325 scheme_sync(1, a);
6326 }
6327
6328 /*--------------------------------------*/
6329 /* Create subprocess */
6330 /*--------------------------------------*/
6331
6332 config = scheme_current_config();
6333
6334 if (SCHEME_FALSEP(group)) {
6335 group = scheme_get_param(config, MZCONFIG_SUBPROC_GROUP_ENABLED);
6336 if (SCHEME_TRUEP(group))
6337 group = new_symbol;
6338 }
6339 if (SAME_OBJ(group, new_symbol))
6340 flags |= RKTIO_PROCESS_NEW_GROUP;
6341
6342 cust_mode = scheme_get_param(config, MZCONFIG_SUBPROC_CUSTODIAN_MODE);
6343 if (SCHEME_SYMBOLP(cust_mode)
6344 && !strcmp(SCHEME_SYM_VAL(cust_mode), "kill")
6345 && (rktio_process_allowed_flags(scheme_rktio) & RKTIO_PROCESS_WINDOWS_CHAIN_TERMINATION))
6346 flags |= RKTIO_PROCESS_WINDOWS_CHAIN_TERMINATION;
6347
6348 current_dir = scheme_get_param(config, MZCONFIG_CURRENT_DIRECTORY);
6349
6350 envvars = scheme_environment_variables_to_envvars(scheme_get_param(config, MZCONFIG_CURRENT_ENV_VARS));
6351
6352 block_timer_signals(1);
6353
6354 result = rktio_process(scheme_rktio,
6355 command, argc, (rktio_const_string_t *)argv,
6356 stdout_fd, stdin_fd, stderr_fd,
6357 (SCHEME_SUBPROCESSP(group) ? ((Scheme_Subprocess *)group)->proc : NULL),
6358 SCHEME_PATH_VAL(current_dir), envvars,
6359 flags);
6360
6361 block_timer_signals(0);
6362
6363 if (need_forget_in) rktio_forget(scheme_rktio, stdin_fd);
6364 if (need_forget_out) rktio_forget(scheme_rktio, stdout_fd);
6365 if (need_forget_err) rktio_forget(scheme_rktio, stderr_fd);
6366
6367 if (envvars)
6368 rktio_envvars_free(scheme_rktio, envvars);
6369
6370 if (!result) {
6371 scheme_raise_exn(MZEXN_FAIL,
6372 "subprocess: process creation failed");
6373 }
6374
6375 /*--------------------------------------*/
6376 /* Create new port objects */
6377 /*--------------------------------------*/
6378
6379 {
6380 Scheme_Object *in = scheme_false, *out = scheme_false, *err = scheme_false;
6381
6382 if (result->stdout_fd)
6383 in = make_fd_input_port(result->stdout_fd, scheme_intern_symbol("subprocess-stdout"), NULL, 0);
6384 if (result->stdin_fd)
6385 out = make_fd_output_port(result->stdin_fd, scheme_intern_symbol("subprocess-stdin"), 0, -1, NULL);
6386 if (result->stderr_fd)
6387 err = make_fd_input_port(result->stderr_fd, scheme_intern_symbol("subprocess-stderr"), NULL, 0);
6388
6389 /*--------------------------------------*/
6390 /* Return result info */
6391 /*--------------------------------------*/
6392
6393 subproc = MALLOC_ONE_TAGGED(Scheme_Subprocess);
6394 subproc->so.type = scheme_subprocess_type;
6395 subproc->proc = result->process;
6396 subproc->is_group_rep = SAME_OBJ(group, new_symbol);
6397 scheme_add_finalizer(subproc, close_subprocess_handle, NULL);
6398
6399 if (SCHEME_TRUEP(cust_mode)) {
6400 Scheme_Custodian_Reference *mref;
6401 Scheme_Close_Custodian_Client *closer;
6402
6403 if (!strcmp(SCHEME_SYM_VAL(cust_mode), "kill"))
6404 closer = kill_subproc;
6405 else
6406 closer = interrupt_subproc;
6407
6408 mref = scheme_add_managed_close_on_exit(NULL, (Scheme_Object *)subproc, closer, NULL);
6409 subproc->mref = mref;
6410 }
6411
6412 free(result);
6413
6414 a[0] = (Scheme_Object *)subproc;
6415 a[1] = in;
6416 a[2] = out;
6417 a[3] = err;
6418
6419 return scheme_values(4, a);
6420 }
6421 }
6422
sch_shell_execute(int c,Scheme_Object * argv[])6423 static Scheme_Object *sch_shell_execute(int c, Scheme_Object *argv[])
6424 {
6425 char *dir;
6426 int show = 0;
6427 int nplen;
6428 Scheme_Object *sv, *sf, *sp;
6429
6430 if (!SCHEME_FALSEP(argv[0]) && !SCHEME_CHAR_STRINGP(argv[0]))
6431 scheme_wrong_contract("shell-execute", "(or/c string? #f)", 0, c, argv);
6432 if (!SCHEME_CHAR_STRINGP(argv[1]))
6433 scheme_wrong_contract("shell-execute", "string?", 1, c, argv);
6434 if (!SCHEME_CHAR_STRINGP(argv[2]))
6435 scheme_wrong_contract("shell-execute", "string?", 2, c, argv);
6436 if (!SCHEME_PATH_STRINGP(argv[3]))
6437 scheme_wrong_contract("shell-execute", "path-string?", 3, c, argv);
6438 {
6439 int show_set = 0;
6440 # define mzseCMP(id, str) \
6441 if (SAME_OBJ(scheme_intern_symbol(str), argv[4]) \
6442 || SAME_OBJ(scheme_intern_symbol(# id), argv[4])) { \
6443 show = RKTIO_ ## id; show_set = 1; }
6444 mzseCMP(SW_HIDE, "sw_hide");
6445 mzseCMP(SW_MAXIMIZE, "sw_maximize");
6446 mzseCMP(SW_MINIMIZE, "sw_minimize");
6447 mzseCMP(SW_RESTORE, "sw_restore");
6448 mzseCMP(SW_SHOW, "sw_show");
6449 mzseCMP(SW_SHOWDEFAULT, "sw_showdefault");
6450 mzseCMP(SW_SHOWMAXIMIZED, "sw_showmaximized");
6451 mzseCMP(SW_SHOWMINIMIZED, "sw_showminimized");
6452 mzseCMP(SW_SHOWMINNOACTIVE, "sw_showminnoactive");
6453 mzseCMP(SW_SHOWNA, "sw_showna");
6454 mzseCMP(SW_SHOWNOACTIVATE, "sw_shownoactivate");
6455 mzseCMP(SW_SHOWNORMAL, "sw_shownormal");
6456
6457 if (!show_set)
6458 scheme_wrong_type("shell-execute", "show-mode symbol", 4, c, argv);
6459 }
6460
6461 dir = scheme_expand_string_filename(argv[3],
6462 "shell-execute", NULL,
6463 SCHEME_GUARD_FILE_EXISTS);
6464
6465 nplen = strlen(dir);
6466 dir = scheme_normal_path_seps(dir, &nplen, 0);
6467
6468 if (SCHEME_FALSEP(argv[0]))
6469 sv = NULL;
6470 else
6471 sv = scheme_char_string_to_byte_string(argv[0]);
6472 sf = scheme_char_string_to_byte_string(argv[1]);
6473 sp = scheme_char_string_to_byte_string(argv[2]);
6474
6475 if (rktio_shell_execute(scheme_rktio,
6476 sv ? SCHEME_BYTE_STR_VAL(sv) : NULL,
6477 SCHEME_BYTE_STR_VAL(sf),
6478 SCHEME_BYTE_STR_VAL(sp),
6479 dir,
6480 show))
6481 return scheme_false;
6482 else {
6483 scheme_raise_exn(MZEXN_FAIL,
6484 "shell-execute: execute failed\n"
6485 " command: %V\n"
6486 " system error: %R",
6487 argv[1]);
6488 return NULL;
6489 }
6490 }
6491
6492 /*========================================================================*/
6493 /* fd reservation */
6494 /*========================================================================*/
6495
6496 /* We don't want on-demand loading of code to fail because we run out of
6497 file descriptors. So, keep one in reserve. */
6498
scheme_reserve_file_descriptor(void)6499 void scheme_reserve_file_descriptor(void)
6500 {
6501 #ifndef DOS_FILE_SYSTEM
6502 if (!fd_reserved) {
6503 the_fd = rktio_open(scheme_rktio, "/dev/null", RKTIO_OPEN_READ);
6504 if (the_fd)
6505 fd_reserved = 1;
6506 }
6507 #endif
6508 }
6509
scheme_release_file_descriptor(void)6510 void scheme_release_file_descriptor(void)
6511 {
6512 #ifndef DOS_FILE_SYSTEM
6513 if (fd_reserved) {
6514 rktio_close(scheme_rktio, the_fd);
6515 fd_reserved = 0;
6516 }
6517 #endif
6518 }
6519
6520 /*========================================================================*/
6521 /* sleeping */
6522 /*========================================================================*/
6523
default_sleep(float v,void * fds)6524 static void default_sleep(float v, void *fds)
6525 {
6526 rktio_sleep(scheme_rktio, v, fds, scheme_semaphore_fd_set);
6527 }
6528
scheme_signal_received_at(void * h)6529 void scheme_signal_received_at(void *h)
6530 XFORM_SKIP_PROC
6531 /* Ensure that Racket wakes up if asleep. */
6532 {
6533 rktio_signal_received_at(h);
6534 }
6535
scheme_get_signal_handle()6536 void *scheme_get_signal_handle()
6537 XFORM_SKIP_PROC
6538 {
6539 return (void *)rktio_get_signal_handle(scheme_rktio);
6540 }
6541
scheme_signal_received(void)6542 void scheme_signal_received(void)
6543 XFORM_SKIP_PROC
6544 {
6545 scheme_signal_received_at(scheme_get_signal_handle());
6546 }
6547
scheme_wait_until_signal_received(void)6548 void scheme_wait_until_signal_received(void)
6549 XFORM_SKIP_PROC
6550 {
6551 rktio_wait_until_signal_received(scheme_rktio);
6552 }
6553
6554 #ifdef USE_WIN32_THREAD_TIMER
6555
6556 typedef struct ITimer_Data {
6557 int done;
6558 HANDLE itimer;
6559 intptr_t delay;
6560 HANDLE semaphore;
6561 HANDLE done_semaphore;
6562 int volatile *fuel_counter_ptr;
6563 uintptr_t volatile *jit_stack_boundary_ptr;
6564 } ITimer_Data;
6565
6566 THREAD_LOCAL_DECL(static ITimer_Data *itimerdata);
6567
ITimer(void * data)6568 static long WINAPI ITimer(void *data)
6569 XFORM_SKIP_PROC
6570 {
6571 ITimer_Data *d = (ITimer_Data *)data;
6572
6573 WaitForSingleObject(d->semaphore, INFINITE);
6574
6575 while (!d->done) {
6576 if (WaitForSingleObject(d->semaphore, d->delay / 1000) == WAIT_TIMEOUT) {
6577 *d->fuel_counter_ptr = 0;
6578 *d->jit_stack_boundary_ptr = (uintptr_t)-1;
6579 if (!d->done)
6580 WaitForSingleObject(d->semaphore, INFINITE);
6581 }
6582 }
6583
6584 ReleaseSemaphore(d->done_semaphore, 1, NULL);
6585
6586 return 0;
6587 }
6588
scheme_start_itimer_thread(intptr_t usec)6589 static void scheme_start_itimer_thread(intptr_t usec)
6590 {
6591 DWORD id;
6592
6593 if (!itimerdata) {
6594 ITimer_Data *d;
6595 HANDLE itimer, sema;
6596
6597 d = malloc(sizeof(ITimer_Data));
6598 memset(d, 0, sizeof(ITimer_Data));
6599
6600 d->fuel_counter_ptr = &scheme_fuel_counter;
6601 d->jit_stack_boundary_ptr = &scheme_jit_stack_boundary;
6602
6603 sema = CreateSemaphore(NULL, 0, 1, NULL);
6604 d->semaphore = sema;
6605 sema = CreateSemaphore(NULL, 0, 1, NULL);
6606 d->done_semaphore = sema;
6607
6608 itimer = CreateThread(NULL, 4096, (LPTHREAD_START_ROUTINE)ITimer,
6609 d, 0, &id);
6610 scheme_remember_thread(itimer, 0);
6611 d->itimer = itimer;
6612
6613 itimerdata = d;
6614 }
6615
6616 itimerdata->delay = usec;
6617 ReleaseSemaphore(itimerdata->semaphore, 1, NULL);
6618 }
6619
scheme_stop_itimer_thread()6620 static void scheme_stop_itimer_thread()
6621 {
6622 ITimer_Data *d = itimerdata;
6623
6624 scheme_forget_thread(d->itimer);
6625
6626 d->done = 1;
6627 ReleaseSemaphore(d->semaphore, 1, NULL);
6628
6629 WaitForSingleObject(d->done_semaphore, INFINITE);
6630
6631 CloseHandle(d->semaphore);
6632 CloseHandle(d->done_semaphore);
6633 CloseHandle(d->itimer);
6634
6635 free(d);
6636 }
6637
6638 #endif
6639
6640 #ifdef USE_PTHREAD_THREAD_TIMER
6641
6642 #include <pthread.h>
6643 #include <unistd.h>
6644 typedef struct ITimer_Data {
6645 int itimer;
6646 int state;
6647 int die;
6648 mz_proc_thread *thread;
6649 pthread_mutex_t mutex;
6650 pthread_cond_t cond;
6651 int delay;
6652 volatile int * fuel_counter_ptr;
6653 volatile uintptr_t * jit_stack_boundary_ptr;
6654 } ITimer_Data;
6655
6656 THREAD_LOCAL_DECL(static ITimer_Data *itimerdata);
6657
green_thread_timer(void * data)6658 static void *green_thread_timer(void *data)
6659 XFORM_SKIP_PROC
6660 {
6661 ITimer_Data *itimer_data;
6662 itimer_data = (ITimer_Data *)data;
6663
6664 while (1) {
6665 if (itimer_data->die)
6666 return NULL;
6667 usleep(itimer_data->delay);
6668 *(itimer_data->fuel_counter_ptr) = 0;
6669 *(itimer_data->jit_stack_boundary_ptr) = (uintptr_t)-1;
6670
6671 pthread_mutex_lock(&itimer_data->mutex);
6672 if (!itimer_data->die) {
6673 if (itimer_data->state) {
6674 itimer_data->state = 0;
6675 } else {
6676 itimer_data->state = -1;
6677 pthread_cond_wait(&itimer_data->cond, &itimer_data->mutex);
6678 }
6679 }
6680 pthread_mutex_unlock(&itimer_data->mutex);
6681 }
6682
6683 return NULL;
6684 }
6685
start_green_thread_timer(intptr_t usec)6686 static void start_green_thread_timer(intptr_t usec)
6687 {
6688 mz_proc_thread *tmp;
6689 itimerdata->die = 0;
6690 itimerdata->delay = usec;
6691 itimerdata->fuel_counter_ptr = &scheme_fuel_counter;
6692 itimerdata->jit_stack_boundary_ptr = &scheme_jit_stack_boundary;
6693 pthread_mutex_init(&itimerdata->mutex, NULL);
6694 pthread_cond_init(&itimerdata->cond, NULL);
6695 tmp = mz_proc_thread_create_w_stacksize(green_thread_timer, itimerdata, 16384);
6696 itimerdata->thread = tmp;
6697 itimerdata->itimer = 1;
6698 }
6699
kill_green_thread_timer()6700 static void kill_green_thread_timer()
6701 {
6702 pthread_mutex_lock(&itimerdata->mutex);
6703 itimerdata->die = 1;
6704 if (!itimerdata->state) {
6705 /* itimer thread is currently running working */
6706 } else if (itimerdata->state < 0) {
6707 /* itimer thread is waiting on cond */
6708 pthread_cond_signal(&itimerdata->cond);
6709 } else {
6710 /* itimer thread is working, and we've already
6711 asked it to continue */
6712 }
6713 pthread_mutex_unlock(&itimerdata->mutex);
6714 (void)mz_proc_thread_wait(itimerdata->thread);
6715 free(itimerdata);
6716 itimerdata = NULL;
6717 }
6718
kickoff_green_thread_timer(intptr_t usec)6719 static void kickoff_green_thread_timer(intptr_t usec)
6720 {
6721 pthread_mutex_lock(&itimerdata->mutex);
6722 itimerdata->delay = usec;
6723 if (!itimerdata->state) {
6724 /* itimer thread is currently running working */
6725 itimerdata->state = 1;
6726 } else if (itimerdata->state < 0) {
6727 /* itimer thread is waiting on cond */
6728 itimerdata->state = 0;
6729 pthread_cond_signal(&itimerdata->cond);
6730 } else {
6731 /* itimer thread is working, and we've already
6732 asked it to continue */
6733 }
6734 pthread_mutex_unlock(&itimerdata->mutex);
6735 }
6736
scheme_start_itimer_thread(intptr_t usec)6737 static void scheme_start_itimer_thread(intptr_t usec)
6738 {
6739 if (!itimerdata) {
6740 itimerdata = (ITimer_Data *)malloc(sizeof(ITimer_Data));
6741 memset(itimerdata, 0, sizeof(ITimer_Data));
6742 }
6743
6744 if (!itimerdata->itimer) {
6745 start_green_thread_timer(usec);
6746 } else {
6747 kickoff_green_thread_timer(usec);
6748 }
6749 }
6750
6751 #endif
6752
6753 #ifdef USE_ITIMER
6754
itimer_expired(int ignored)6755 static void itimer_expired(int ignored)
6756 XFORM_SKIP_PROC
6757 {
6758 scheme_fuel_counter = 0;
6759 scheme_jit_stack_boundary = (uintptr_t)-1;
6760 }
6761
kickoff_itimer(intptr_t usec)6762 static void kickoff_itimer(intptr_t usec)
6763 XFORM_SKIP_PROC
6764 {
6765 struct itimerval t;
6766 struct itimerval old;
6767 static int itimer_handler_installed = 0;
6768
6769 if (!itimer_handler_installed) {
6770 itimer_handler_installed = 1;
6771 scheme_set_signal_handler(SIGPROF, itimer_expired);
6772 }
6773
6774 t.it_value.tv_sec = 0;
6775 t.it_value.tv_usec = usec;
6776 t.it_interval.tv_sec = 0;
6777 t.it_interval.tv_usec = 0;
6778
6779 setitimer(ITIMER_PROF, &t, &old);
6780 }
6781
block_timer_signals(int block)6782 static void block_timer_signals(int block)
6783 /* Doesn't actually block the signal, because we don't want
6784 a new subprocess to start with the signal blocked,
6785 but turns off the timer and makes sure that no signal
6786 is pending. */
6787 {
6788 static intptr_t saved_usec;
6789
6790 if (block) {
6791 struct itimerval t, old;
6792 sigset_t sigs;
6793
6794 t.it_value.tv_sec = 0;
6795 t.it_value.tv_usec = 0;
6796 t.it_interval.tv_sec = 0;
6797 t.it_interval.tv_usec = 0;
6798
6799 setitimer(ITIMER_PROF, &t, &old);
6800
6801 saved_usec = old.it_value.tv_usec;
6802
6803 /* Clear already-queued PROF signal, if any
6804 --- unlikely, but possible */
6805 sigemptyset(&sigs);
6806 while (!sigpending(&sigs)) {
6807 if (sigismember(&sigs, SIGPROF)) {
6808 sigprocmask(SIG_SETMASK, NULL, &sigs);
6809 sigdelset(&sigs, SIGPROF);
6810 sigsuspend(&sigs);
6811 sigemptyset(&sigs);
6812 } else
6813 break;
6814 }
6815 } else {
6816 kickoff_itimer(saved_usec);
6817 }
6818 }
6819
6820 #else
6821
block_timer_signals(int block)6822 static void block_timer_signals(int block) { }
6823
6824 #endif
6825
scheme_kickoff_green_thread_time_slice_timer(intptr_t usec)6826 void scheme_kickoff_green_thread_time_slice_timer(intptr_t usec) {
6827 #ifdef USE_ITIMER
6828 kickoff_itimer(usec);
6829 #elif defined(USE_WIN32_THREAD_TIMER)
6830 scheme_start_itimer_thread(usec);
6831 #elif defined(USE_PTHREAD_THREAD_TIMER)
6832 scheme_start_itimer_thread(usec);
6833 #endif
6834 }
6835
scheme_kill_green_thread_timer()6836 void scheme_kill_green_thread_timer()
6837 {
6838 #if defined(USE_PTHREAD_THREAD_TIMER)
6839 kill_green_thread_timer();
6840 #elif defined(USE_WIN32_THREAD_TIMER)
6841 scheme_stop_itimer_thread();
6842 #endif
6843 }
6844
6845 #ifdef OS_X
6846
scheme_start_sleeper_thread(void (* ignored_sleep)(float seconds,void * fds),float secs,void * fds,int hit_fd)6847 void scheme_start_sleeper_thread(void (*ignored_sleep)(float seconds, void *fds), float secs, void *fds, int hit_fd)
6848 XFORM_SKIP_PROC
6849 {
6850 rktio_start_sleep(scheme_rktio, secs, fds, scheme_semaphore_fd_set, hit_fd);
6851 }
6852
scheme_end_sleeper_thread()6853 void scheme_end_sleeper_thread()
6854 XFORM_SKIP_PROC
6855 {
6856 rktio_end_sleep(scheme_rktio);
6857 }
6858
6859 #else
6860
scheme_start_sleeper_thread(void (* given_sleep)(float seconds,void * fds),float secs,void * fds,int hit_fd)6861 void scheme_start_sleeper_thread(void (*given_sleep)(float seconds, void *fds), float secs, void *fds, int hit_fd)
6862 {
6863 }
scheme_end_sleeper_thread()6864 void scheme_end_sleeper_thread()
6865 {
6866 }
6867
6868 #endif
6869
6870 /*========================================================================*/
6871 /* thread helper */
6872 /*========================================================================*/
6873
6874 /* The scheme_call_sequence() functionc an be used, with some care,
6875 via the FFI to run a computation in a foreign thread and thread
6876 results through. Keeping the number of procedures below
6877 `NUM_COPIED_SEQUENCE_PROCS` can potentially simplify things, too */
6878
6879 #define NUM_COPIED_SEQUENCE_PROCS 5
6880
6881 typedef void *(*Scheme_Sequenced_Proc)(void *);
6882
6883 struct Scheme_Proc_Sequence {
6884 Scheme_Object *num_procs; /* pointer simplifies allocation issues */
6885 void *init_data;
6886 Scheme_Sequenced_Proc p[mzFLEX_ARRAY_DECL];
6887 };
6888
scheme_call_sequence_of_procedures(struct Scheme_Proc_Sequence * s)6889 void *scheme_call_sequence_of_procedures(struct Scheme_Proc_Sequence *s)
6890 XFORM_SKIP_PROC
6891 {
6892 int i, num_procs = SCHEME_INT_VAL(s->num_procs);
6893 void *data = s->init_data;
6894 Scheme_Sequenced_Proc copied[NUM_COPIED_SEQUENCE_PROCS];
6895
6896 if (num_procs <= NUM_COPIED_SEQUENCE_PROCS) {
6897 for (i = 0; i < num_procs; i++) {
6898 copied[i] = s->p[i];
6899 }
6900 }
6901
6902 for (i = 0; i < num_procs; i++) {
6903 if (num_procs <= NUM_COPIED_SEQUENCE_PROCS)
6904 data = copied[i](data);
6905 else
6906 data = s->p[i](data);
6907 }
6908
6909 return data;
6910 }
6911
6912 /*========================================================================*/
6913 /* memory debugging help */
6914 /*========================================================================*/
6915
6916
6917 #ifdef MEMORY_COUNTING_ON
scheme_count_input_port(Scheme_Object * port,intptr_t * s,intptr_t * e,Scheme_Hash_Table * ht)6918 void scheme_count_input_port(Scheme_Object *port, intptr_t *s, intptr_t *e,
6919 Scheme_Hash_Table *ht)
6920 {
6921 Scheme_Input_Port *ip;
6922
6923 ip = scheme_input_port_record(port);
6924
6925 *e = (ht ? scheme_count_memory(ip->read_handler, ht) : 0);
6926 *s = sizeof(Scheme_Input_Port);
6927
6928 if (ip->sub_type == file_input_port_type)
6929 *s += sizeof(Scheme_Input_File);
6930 else if (ip->sub_type == scheme_string_input_port_type) {
6931 Scheme_Indexed_String *is;
6932 is = (Scheme_Indexed_String *)ip->port_data;
6933 *s += (sizeof(Scheme_Indexed_String)
6934 + is->size);
6935 } else if (ip->sub_type == scheme_tcp_input_port_type) {
6936 if (ht && !scheme_hash_get(ht, (Scheme_Object *)ip->port_data)) {
6937 scheme_hash_set(ht, (Scheme_Object *)ip->port_data, scheme_true);
6938 }
6939 } else if (ip->sub_type == scheme_user_input_port_type) {
6940 Scheme_Object **d;
6941 d = (Scheme_Object **)ip->port_data;
6942 *s += (3 * sizeof(Scheme_Object *));
6943 *e += (ht
6944 ? (scheme_count_memory(d[0], ht)
6945 + scheme_count_memory(d[1], ht)
6946 + scheme_count_memory(d[2], ht))
6947 : 0);
6948 } else if (ip->sub_type == scheme_pipe_read_port_type) {
6949 if (ht && !scheme_hash_get(ht, (Scheme_Object *)ip->port_data)) {
6950 Scheme_Pipe *p = (Scheme_Pipe *)ip->port_data;
6951 scheme_hash_set(ht, (Scheme_Object *)ip->port_data, scheme_true);
6952 *s += (sizeof(Scheme_Pipe) + p->buflen);
6953 }
6954 }
6955 }
6956
scheme_count_output_port(Scheme_Object * port,intptr_t * s,intptr_t * e,Scheme_Hash_Table * ht)6957 void scheme_count_output_port(Scheme_Object *port, intptr_t *s, intptr_t *e,
6958 Scheme_Hash_Table *ht)
6959 {
6960 Scheme_Output_Port *op;
6961
6962 op = scheme_output_port_record(port);
6963
6964 *e = 0;
6965 *s = sizeof(Scheme_Output_Port);
6966
6967 if (op->sub_type == file_output_port_type)
6968 *s += sizeof(Scheme_Output_File);
6969 else if (op->sub_type == scheme_string_output_port_type) {
6970 Scheme_Indexed_String *is;
6971 is = (Scheme_Indexed_String *)op->port_data;
6972 *s += (sizeof(Scheme_Indexed_String)
6973 + is->size);
6974 } else if (op->sub_type == scheme_tcp_output_port_type) {
6975 if (!scheme_hash_get(ht, (Scheme_Object *)op->port_data)) {
6976 scheme_hash_set(ht, (Scheme_Object *)op->port_data, scheme_true);
6977 }
6978 } else if (op->sub_type == scheme_user_output_port_type) {
6979 Scheme_Object **d;
6980 d = (Scheme_Object **)op->port_data;
6981 *s += (2 * sizeof(Scheme_Object *));
6982 *e += (ht
6983 ? (scheme_count_memory(d[0], ht)
6984 + scheme_count_memory(d[1], ht))
6985 : 0);
6986 } else if (op->sub_type == scheme_pipe_read_port_type) {
6987 if (!scheme_hash_get(ht, (Scheme_Object *)op->port_data)) {
6988 Scheme_Pipe *p = (Scheme_Pipe *)op->port_data;
6989 scheme_hash_set(ht, (Scheme_Object *)op->port_data, scheme_true);
6990 *s += (sizeof(Scheme_Pipe) + p->buflen);
6991 }
6992 }
6993 }
6994 #endif
6995
6996 /*========================================================================*/
6997 /* precise GC traversers */
6998 /*========================================================================*/
6999
7000 #ifdef MZ_PRECISE_GC
7001
7002 START_XFORM_SKIP;
7003
7004 #include "mzmark_port.inc"
7005
register_traversers(void)7006 static void register_traversers(void)
7007 {
7008 GC_REG_TRAV(scheme_rt_input_file, mark_input_file);
7009 GC_REG_TRAV(scheme_rt_output_file, mark_output_file);
7010
7011 GC_REG_TRAV(scheme_rt_input_fd, mark_input_fd);
7012
7013 GC_REG_TRAV(scheme_subprocess_type, mark_subprocess);
7014 GC_REG_TRAV(scheme_write_evt_type, mark_read_write_evt);
7015
7016 GC_REG_TRAV(scheme_filesystem_change_evt_type, mark_filesystem_change_evt);
7017 }
7018
7019 END_XFORM_SKIP;
7020
7021 #endif
7022