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), (&current_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), (&current_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