1 /* -*-C-*-
2
3 Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994,
4 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
5 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Massachusetts
6 Institute of Technology
7
8 This file is part of MIT/GNU Scheme.
9
10 MIT/GNU Scheme is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or (at
13 your option) any later version.
14
15 MIT/GNU Scheme is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with MIT/GNU Scheme; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
23 USA.
24
25 */
26
27 #include "os2.h"
28 #include "os2proc.h"
29
30 extern void add_reload_cleanup (void (*) (void));
31 extern void OS2_initialize_console_channel (Tchannel);
32 extern void OS2_initialize_pipe_channel (Tchannel);
33
34 static enum channel_type handle_channel_type (LHANDLE);
35 static void handle_noinherit (LHANDLE);
36
37 Tchannel OS_channel_table_size;
38 struct channel * OS2_channel_table;
39 Tchannel * OS2_channel_pointer_table;
40 const int OS_have_select_p = 1;
41
42 #ifndef OS2_DEFAULT_MAX_FH
43 # define OS2_DEFAULT_MAX_FH 256
44 #endif
45
46 /* Set this to a larger size than OS2_DEFAULT_MAX_FH, because the
47 maximum number of file handles can be increased dynamically by
48 calling a primitive. */
49 #ifndef OS2_DEFAULT_CHANNEL_TABLE_SIZE
50 # define OS2_DEFAULT_CHANNEL_TABLE_SIZE 1024
51 #endif
52
53 void
OS2_initialize_channels(void)54 OS2_initialize_channels (void)
55 {
56 {
57 LONG req_max_fh = 0;
58 ULONG current_max_fh;
59 STD_API_CALL (dos_set_rel_max_fh, ((&req_max_fh), (¤t_max_fh)));
60 req_max_fh = (OS2_DEFAULT_MAX_FH - current_max_fh);
61 if (req_max_fh > 0)
62 STD_API_CALL (dos_set_rel_max_fh, ((&req_max_fh), (¤t_max_fh)));
63 }
64 OS_channel_table_size = OS2_DEFAULT_CHANNEL_TABLE_SIZE;
65 OS2_channel_table =
66 (OS_malloc (OS_channel_table_size * (sizeof (struct channel))));
67 OS2_channel_pointer_table =
68 (OS_malloc (OS_channel_table_size * (sizeof (Tchannel))));
69 {
70 Tchannel channel;
71 for (channel = 0; (channel < OS_channel_table_size); channel += 1)
72 {
73 (CHANNEL_OPEN (channel)) = 0;
74 (OS2_channel_pointer_table [channel]) = channel;
75 }
76 }
77 add_reload_cleanup (OS2_channel_close_all_noerror);
78 }
79
80 void
OS2_reset_channels(void)81 OS2_reset_channels (void)
82 {
83 OS_free (OS2_channel_table);
84 OS2_channel_table = 0;
85 OS_channel_table_size = 0;
86 }
87
88 void
OS2_channel_operation(Tchannel channel,chop_t operation,choparg_t arg1,choparg_t arg2,choparg_t arg3)89 OS2_channel_operation (Tchannel channel, chop_t operation,
90 choparg_t arg1, choparg_t arg2, choparg_t arg3)
91 {
92 ((* (CHANNEL_OPERATOR (channel))) (channel, operation, arg1, arg2, arg3));
93 }
94
95 Tchannel
OS2_make_channel(LHANDLE handle,unsigned int mode)96 OS2_make_channel (LHANDLE handle, unsigned int mode)
97 {
98 Tchannel channel;
99 enum channel_type type;
100 transaction_begin ();
101 OS2_handle_close_on_abort (handle);
102 type = (handle_channel_type (handle));
103 handle_noinherit (handle);
104 channel = (OS2_allocate_channel ());
105 OS2_initialize_channel (channel, handle, mode, type);
106 switch (type)
107 {
108 case channel_type_console:
109 OS2_initialize_console_channel (channel);
110 break;
111 case channel_type_unnamed_pipe:
112 OS2_initialize_pipe_channel (channel);
113 break;
114 }
115 transaction_commit ();
116 return (channel);
117 }
118
119 Tchannel
OS2_allocate_channel(void)120 OS2_allocate_channel (void)
121 {
122 Tchannel channel = 0;
123 while (1)
124 {
125 if (channel == OS_channel_table_size)
126 OS2_error_out_of_channels ();
127 if (! (CHANNEL_OPEN (channel)))
128 return (channel);
129 channel += 1;
130 }
131 }
132
133 static enum channel_type
handle_channel_type(LHANDLE handle)134 handle_channel_type (LHANDLE handle)
135 {
136 /* **** For now, limit channel types to those that we know how to
137 handle in a reasonable way. Later we can add other types if
138 needed. However, we probably won't need other types since pipes
139 and files are sufficient to do nearly anything, and the console
140 will be flushed when the PM support is installed. */
141 ULONG type;
142 ULONG flags;
143 if ((dos_query_h_type (handle, (&type), (&flags))) == NO_ERROR)
144 switch (type & 0xff)
145 {
146 case FHT_DISKFILE:
147 return (channel_type_file);
148 case FHT_CHRDEV:
149 if ((flags & 0x3) != 0)
150 return (channel_type_console);
151 else if ((flags & 0x4) != 0)
152 /* return (channel_type_null); */
153 break;
154 else if ((flags & 0x8) != 0)
155 /* return (channel_type_clock); */
156 break;
157 else
158 /* return (channel_type_character_device); */
159 break;
160 case FHT_PIPE:
161 {
162 APIRET rc = (dos_query_n_p_h_state (handle, (&flags)));
163 if ((rc == NO_ERROR) || (rc == ERROR_PIPE_NOT_CONNECTED))
164 /* return (channel_type_named_pipe); */
165 break;
166 else
167 return (channel_type_unnamed_pipe);
168 }
169 }
170 /* Anything that can't be recognized should be treated as a pipe.
171 This is safe since pipes aren't assumed to have any special
172 properties. */
173 return (channel_type_unnamed_pipe);
174 }
175
176 static void
handle_noinherit(LHANDLE handle)177 handle_noinherit (LHANDLE handle)
178 {
179 ULONG state;
180 STD_API_CALL (dos_query_fh_state, (handle, (& state)));
181 /* Magic mask 0xFF88 zeroes out high bits and two fields
182 required to be zero by the spec. When testing, the high
183 bits were not zero, and this caused the system call to
184 complain. */
185 state &= 0xFF88;
186 STD_API_CALL
187 (dos_set_fh_state, (handle, (state | OPEN_FLAGS_NOINHERIT)));
188 }
189
190 static void
channel_discard_on_abort_1(void * cp)191 channel_discard_on_abort_1 (void * cp)
192 {
193 (CHANNEL_OPEN (* ((Tchannel *) cp))) = 0;
194 }
195
196 static void
channel_discard_on_abort(Tchannel c)197 channel_discard_on_abort (Tchannel c)
198 {
199 Tchannel * cp = (dstack_alloc (sizeof (Tchannel)));
200 (*cp) = c;
201 transaction_record_action (tat_abort, channel_discard_on_abort_1, cp);
202 }
203
204 void
OS2_initialize_channel(Tchannel channel,LHANDLE handle,unsigned int mode,enum channel_type type)205 OS2_initialize_channel (Tchannel channel, LHANDLE handle, unsigned int mode,
206 enum channel_type type)
207 {
208 (CHANNEL_HANDLE (channel)) = handle;
209 (CHANNEL_TYPE (channel)) = type;
210 (CHANNEL_OPEN (channel)) = 1;
211 (CHANNEL_INTERNAL (channel)) = 0;
212 (CHANNEL_NONBLOCKING (channel)) = 0;
213 (CHANNEL_INPUTP (channel)) = ((mode & CHANNEL_READ) != 0);
214 (CHANNEL_OUTPUTP (channel)) = ((mode & CHANNEL_WRITE) != 0);
215 (CHANNEL_OPERATOR (channel)) = 0;
216 channel_discard_on_abort (channel);
217 }
218
219 void
OS_channel_close(Tchannel channel)220 OS_channel_close (Tchannel channel)
221 {
222 if (! (CHANNEL_INTERNAL (channel)))
223 {
224 if (CHANNEL_ABSTRACT_P (channel))
225 OS2_channel_operation (channel, chop_close, 0, 0, 0);
226 else
227 STD_API_CALL (dos_close, (CHANNEL_HANDLE (channel)));
228 (CHANNEL_OPEN (channel)) = 0;
229 }
230 }
231
232 void
OS2_channel_close_all_noerror(void)233 OS2_channel_close_all_noerror (void)
234 {
235 Tchannel channel;
236 for (channel = 0; (channel < OS_channel_table_size); channel += 1)
237 if (CHANNEL_OPEN (channel))
238 OS_channel_close_noerror (channel);
239 }
240
241 void
OS_channel_close_noerror(Tchannel channel)242 OS_channel_close_noerror (Tchannel channel)
243 {
244 transaction_begin ();
245 OS2_ignore_errors ();
246 OS_channel_close (channel);
247 transaction_commit ();
248 }
249
250 static void
OS_channel_close_on_abort_1(void * cp)251 OS_channel_close_on_abort_1 (void * cp)
252 {
253 OS_channel_close_noerror (* ((Tchannel *) cp));
254 }
255
256 void
OS_channel_close_on_abort(Tchannel channel)257 OS_channel_close_on_abort (Tchannel channel)
258 {
259 Tchannel * cp = (dstack_alloc (sizeof (Tchannel)));
260 (*cp) = (channel);
261 transaction_record_action (tat_abort, OS_channel_close_on_abort_1, cp);
262 }
263
264 static void
OS2_handle_close_on_abort_1(void * hp)265 OS2_handle_close_on_abort_1 (void * hp)
266 {
267 (void) dos_close (* ((LHANDLE *) hp));
268 }
269
270 void
OS2_handle_close_on_abort(LHANDLE h)271 OS2_handle_close_on_abort (LHANDLE h)
272 {
273 LHANDLE * hp = (dstack_alloc (sizeof (LHANDLE)));
274 (*hp) = h;
275 transaction_record_action (tat_abort, OS2_handle_close_on_abort_1, hp);
276 }
277
278 int
OS_channel_open_p(Tchannel channel)279 OS_channel_open_p (Tchannel channel)
280 {
281 return (CHANNEL_OPEN (channel));
282 }
283
284 enum channel_type
OS_channel_type(Tchannel channel)285 OS_channel_type (Tchannel channel)
286 {
287 return (CHANNEL_TYPE (channel));
288 }
289
290 long
OS_channel_read(Tchannel channel,void * buffer,size_t nbytes)291 OS_channel_read (Tchannel channel, void * buffer, size_t nbytes)
292 {
293 long n;
294 if (nbytes == 0)
295 return (0);
296 if (CHANNEL_ABSTRACT_P (channel))
297 OS2_channel_operation (channel, chop_read,
298 ((choparg_t) buffer),
299 ((choparg_t) nbytes),
300 ((choparg_t) (& n)));
301 else
302 STD_API_CALL
303 (dos_read, ((CHANNEL_HANDLE (channel)), buffer, nbytes,
304 ((ULONG *) (& n))));
305 return (n);
306 }
307
308 long
OS_channel_write(Tchannel channel,const void * buffer,size_t nbytes)309 OS_channel_write (Tchannel channel, const void * buffer, size_t nbytes)
310 {
311 long n;
312 if (nbytes == 0)
313 return (0);
314 if (CHANNEL_ABSTRACT_P (channel))
315 OS2_channel_operation (channel,
316 chop_write,
317 ((choparg_t) buffer),
318 ((choparg_t) nbytes),
319 ((choparg_t) (& n)));
320 else
321 STD_API_CALL
322 (dos_write, ((CHANNEL_HANDLE (channel)), ((void *) buffer), nbytes,
323 ((ULONG *) (& n))));
324 return (n);
325 }
326
327 int
OS_channel_nonblocking_p(Tchannel channel)328 OS_channel_nonblocking_p (Tchannel channel)
329 {
330 return (CHANNEL_NONBLOCKING (channel));
331 }
332
333 void
OS_channel_nonblocking(Tchannel channel)334 OS_channel_nonblocking (Tchannel channel)
335 {
336 (CHANNEL_NONBLOCKING (channel)) = 1;
337 }
338
339 void
OS_channel_blocking(Tchannel channel)340 OS_channel_blocking (Tchannel channel)
341 {
342 (CHANNEL_NONBLOCKING (channel)) = 0;
343 }
344
345 void
OS_channel_synchronize(Tchannel channel)346 OS_channel_synchronize (Tchannel channel)
347 {
348 }
349
350 size_t
OS_channel_read_load_file(Tchannel channel,void * buffer,size_t nbytes)351 OS_channel_read_load_file (Tchannel channel, void * buffer, size_t nbytes)
352 {
353 ULONG nread;
354 if ((dos_read ((CHANNEL_HANDLE (channel)), buffer, nbytes, (&nread))) != 0)
355 return (0);
356 return (nread);
357 }
358
359 size_t
OS_channel_write_dump_file(Tchannel channel,const void * buffer,size_t nbytes)360 OS_channel_write_dump_file (Tchannel channel,
361 const void * buffer,
362 size_t nbytes)
363 {
364 ULONG nwrite;
365 if ((dos_write
366 ((CHANNEL_HANDLE (channel)), ((void *) buffer), nbytes, (&nwrite)))
367 != 0)
368 return (0);
369 return (nwrite);
370 }
371
372 void
OS_channel_write_string(Tchannel channel,const char * string)373 OS_channel_write_string (Tchannel channel, const char * string)
374 {
375 unsigned long length = (strlen (string));
376 if ((OS_channel_write (channel, string, length)) != length)
377 OS2_error_anonymous ();
378 }
379
380 struct select_registry_s
381 {
382 unsigned int n_qids;
383 unsigned int length;
384 qid_t * qids;
385 unsigned char * qmodes;
386 unsigned char * rmodes;
387 };
388
389 select_registry_t
OS_allocate_select_registry(void)390 OS_allocate_select_registry (void)
391 {
392 struct select_registry_s * r
393 = (OS_malloc (sizeof (struct select_registry_s)));
394 (r -> n_qids) = 0;
395 (r -> length) = 16;
396 (r -> qids) = (OS_malloc ((sizeof (qid_t)) * (r -> length)));
397 (r -> qmodes) = (OS_malloc ((sizeof (unsigned char)) * (r -> length)));
398 (r -> rmodes) = (OS_malloc ((sizeof (unsigned char)) * (r -> length)));
399 return (r);
400 }
401
402 void
OS_deallocate_select_registry(select_registry_t registry)403 OS_deallocate_select_registry (select_registry_t registry)
404 {
405 struct select_registry_s * r = registry;
406 OS_free (r -> rmodes);
407 OS_free (r -> qmodes);
408 OS_free (r -> qids);
409 OS_free (r);
410 }
411
412 static void
resize_select_registry(struct select_registry_s * r,int growp)413 resize_select_registry (struct select_registry_s * r, int growp)
414 {
415 if (growp)
416 (r -> length) *= 2;
417 else
418 (r -> length) /= 2;
419 (r -> qids)
420 = (OS_realloc ((r -> qids),
421 ((sizeof (qid_t)) * (r -> length))));
422 (r -> qmodes)
423 = (OS_realloc ((r -> qmodes),
424 ((sizeof (unsigned char)) * (r -> length))));
425 (r -> rmodes)
426 = (OS_realloc ((r -> rmodes),
427 ((sizeof (unsigned char)) * (r -> length))));
428 }
429
430 void
OS_add_to_select_registry(select_registry_t registry,int fd,unsigned int mode)431 OS_add_to_select_registry (select_registry_t registry, int fd,
432 unsigned int mode)
433 {
434 struct select_registry_s * r = registry;
435 qid_t qid = fd;
436 unsigned int i = 0;
437
438 while (i < (r -> n_qids))
439 {
440 if (((r -> qids) [i]) == qid)
441 {
442 ((r -> qmodes) [i]) |= mode;
443 return;
444 }
445 i += 1;
446 }
447 if (i == (r -> length))
448 resize_select_registry (r, 1);
449 ((r -> qids) [i]) = qid;
450 ((r -> qmodes) [i]) = mode;
451 (r -> n_qids) += 1;
452 }
453
454 void
OS_remove_from_select_registry(select_registry_t registry,int fd,unsigned int mode)455 OS_remove_from_select_registry (select_registry_t registry, int fd,
456 unsigned int mode)
457 {
458 struct select_registry_s * r = registry;
459 qid_t qid = fd;
460 unsigned int i = 0;
461
462 while (1)
463 {
464 if (i == (r -> n_qids))
465 return;
466 if (((r -> qids) [i]) == qid)
467 {
468 ((r -> qmodes) [i]) &=~ mode;
469 if (((r -> qmodes) [i]) == 0)
470 break;
471 else
472 return;
473 }
474 i += 1;
475 }
476 while (i < (r -> n_qids))
477 {
478 ((r -> qids) [i]) = ((r -> qids) [(i + 1)]);
479 ((r -> qmodes) [i]) = ((r -> qmodes) [(i + 1)]);
480 i += 1;
481 }
482 (r -> n_qids) -= 1;
483
484 if (((r -> length) > 16) && ((r -> n_qids) < ((r -> length) / 2)))
485 resize_select_registry (r, 0);
486 }
487
488 unsigned int
OS_select_registry_length(select_registry_t registry)489 OS_select_registry_length (select_registry_t registry)
490 {
491 struct select_registry_s * r = registry;
492 return (r -> n_qids);
493 }
494
495 void
OS_select_registry_result(select_registry_t registry,unsigned int index,int * fd_r,unsigned int * mode_r)496 OS_select_registry_result (select_registry_t registry, unsigned int index,
497 int * fd_r, unsigned int * mode_r)
498 {
499 struct select_registry_s * r = registry;
500 (*fd_r) = ((r -> qids) [index]);
501 (*mode_r) = ((r -> rmodes) [index]);
502 }
503
504 int
OS_test_select_descriptor(int fd,int blockp,unsigned int qmode)505 OS_test_select_descriptor (int fd, int blockp, unsigned int qmode)
506 {
507 qid_t qid = fd;
508 unsigned int rmode = (qmode & SELECT_MODE_WRITE);
509 if ((qmode & SELECT_MODE_READ) == 0)
510 return (rmode);
511 switch (OS2_message_availablep (qid, blockp))
512 {
513 case mat_available:
514 return (rmode | SELECT_MODE_READ);
515 case mat_not_available:
516 return (rmode);
517 case mat_interrupt:
518 return
519 ((OS_process_any_status_change ())
520 ? SELECT_PROCESS_STATUS_CHANGE
521 : SELECT_INTERRUPT);
522 default:
523 error_external_return ();
524 return (rmode | SELECT_MODE_ERROR);
525 }
526 }
527
528 int
OS_test_select_registry(select_registry_t registry,int blockp)529 OS_test_select_registry (select_registry_t registry, int blockp)
530 {
531 struct select_registry_s * r = registry;
532 unsigned int n_values = 0;
533 int interruptp = 0;
534 unsigned int i;
535
536 while (1)
537 {
538 for (i = 0; (i < (r -> n_qids)); i += 1)
539 {
540 ((r -> rmodes) [i]) = (((r -> qmodes) [i]) & SELECT_MODE_WRITE);
541 if ((((r -> qmodes) [i]) & SELECT_MODE_READ) != 0)
542 switch (OS2_message_availablep (((r -> qids) [i]), 0))
543 {
544 case mat_available:
545 ((r -> rmodes) [i]) |= SELECT_MODE_READ;
546 break;
547 case mat_interrupt:
548 interruptp = 1;
549 break;
550 }
551 if (((r -> rmodes) [i]) != 0)
552 n_values += 1;
553 }
554 if (n_values > 0)
555 return (n_values);
556 if (interruptp)
557 return
558 ((OS_process_any_status_change ())
559 ? SELECT_PROCESS_STATUS_CHANGE
560 : SELECT_INTERRUPT);
561 if (!blockp)
562 return (0);
563 if ((OS2_scheme_tqueue_block ()) == mat_interrupt)
564 interruptp = 1;
565 }
566 }
567
568 int
OS_pause(void)569 OS_pause (void)
570 {
571 /* Wait-for-io must spin. */
572 return
573 ((OS_process_any_status_change ())
574 ? SELECT_PROCESS_STATUS_CHANGE
575 : SELECT_INTERRUPT);
576 }
577