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