io.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. /* Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees.
  2. See file COPYING. */
  3. /*
  4. * Scheme 48/POSIX I/O interface
  5. */
  6. #include <stdio.h>
  7. #include <sys/types.h>
  8. #include <unistd.h>
  9. #include <fcntl.h>
  10. #include <errno.h>
  11. #include "scheme48.h"
  12. #include "scheme48vm.h" /* ps_close_fd() */
  13. #include "posix.h"
  14. #include "c-mods.h"
  15. #include "unix.h"
  16. #include "fd-io.h"
  17. extern void s48_init_posix_io(void);
  18. static s48_value posix_dup(s48_value channel, s48_value new_mode),
  19. posix_dup2(s48_value channel, s48_value new_fd),
  20. posix_pipe(void),
  21. posix_close_on_exec_p(s48_value channel),
  22. posix_set_close_on_exec(s48_value channel,
  23. s48_value close_p),
  24. posix_io_flags(s48_value channel, s48_value options);
  25. static s48_value s48_enter_file_options(int options);
  26. /*
  27. * Record types imported from Scheme.
  28. */
  29. static s48_value posix_file_options_enum_set_type_binding = S48_FALSE;
  30. /*
  31. * Install all exported functions in Scheme48.
  32. */
  33. void
  34. s48_init_posix_io(void)
  35. {
  36. S48_EXPORT_FUNCTION(posix_dup);
  37. S48_EXPORT_FUNCTION(posix_dup2);
  38. S48_EXPORT_FUNCTION(posix_pipe);
  39. S48_EXPORT_FUNCTION(posix_close_on_exec_p);
  40. S48_EXPORT_FUNCTION(posix_set_close_on_exec);
  41. S48_EXPORT_FUNCTION(posix_io_flags);
  42. S48_GC_PROTECT_GLOBAL(posix_file_options_enum_set_type_binding);
  43. posix_file_options_enum_set_type_binding =
  44. s48_get_imported_binding("posix-file-options-enum-set-type");
  45. }
  46. /*
  47. * Moves `channel' to a new file descriptor and returns a new channel that uses
  48. * `channel''s old file descriptor.
  49. *
  50. * Without all the error checking, this is:
  51. * old_fd = channel_os_index(channel);
  52. * new_fd = dup(old_fd);
  53. * s48_set_channel_os_index(channel, new_fd);
  54. * return s48_add_channel(old_fd);
  55. *
  56. */
  57. static s48_value
  58. posix_dup(s48_value channel, s48_value new_mode)
  59. {
  60. int new_fd, old_fd, flags;
  61. long status;
  62. s48_value old_mode;
  63. s48_value new_channel;
  64. if (!S48_CHANNEL_P(channel) ||
  65. S48_CHANNEL_STATUS(channel) == S48_CHANNEL_STATUS_CLOSED)
  66. s48_raise_argument_type_error(channel);
  67. old_fd = S48_UNSAFE_EXTRACT_FIXNUM(S48_UNSAFE_CHANNEL_OS_INDEX(channel));
  68. old_mode = S48_UNSAFE_CHANNEL_STATUS(channel);
  69. RETRY_OR_RAISE_NEG(new_fd, dup(old_fd));
  70. status = s48_set_channel_os_index(channel, new_fd);
  71. if (status != S48_TRUE) {
  72. ps_close_fd(new_fd); /* retries if interrupted */
  73. s48_raise_scheme_exception(s48_extract_fixnum(status), 1, channel); }
  74. if (new_mode == S48_CHANNEL_STATUS_OUTPUT
  75. && old_mode == S48_CHANNEL_STATUS_INPUT) {
  76. RETRY_OR_RAISE_NEG(flags, fcntl(new_fd, F_GETFL));
  77. RETRY_OR_RAISE_NEG(status, fcntl(new_fd, F_SETFL, flags | O_NONBLOCK)); }
  78. new_channel = s48_add_channel((new_mode == S48_FALSE) ? old_mode : new_mode,
  79. S48_UNSAFE_CHANNEL_ID(channel),
  80. old_fd);
  81. if (!S48_CHANNEL_P(new_channel)) {
  82. ps_close_fd(old_fd); /* retries if interrupted */
  83. s48_raise_scheme_exception(s48_extract_fixnum(new_channel), 1, channel); }
  84. return new_channel;
  85. }
  86. /*
  87. * Same again, except that we get told what the new file descriptor is to be.
  88. * We close the channel currently using that descriptor, if there be one.
  89. *
  90. * Without all the error checking, this is:
  91. * old_fd = channel_os_index(channel);
  92. * dup2(old_fd, new_fd);
  93. * s48_set_channel_os_index(channel, new_fd);
  94. * return s48_add_channel(old_fd);
  95. */
  96. static s48_value
  97. posix_dup2(s48_value channel, s48_value new_fd)
  98. {
  99. s48_value new_channel;
  100. long s48_status;
  101. int status;
  102. int new_c_fd, old_c_fd;
  103. if (!S48_CHANNEL_P(channel) ||
  104. S48_CHANNEL_STATUS(channel) == S48_CHANNEL_STATUS_CLOSED)
  105. s48_raise_argument_type_error(channel);
  106. if (!S48_FIXNUM_P(new_fd) || new_fd < 0)
  107. s48_raise_argument_type_error(new_fd);
  108. old_c_fd = s48_extract_fixnum(S48_UNSAFE_CHANNEL_OS_INDEX(channel));
  109. new_c_fd = s48_extract_fixnum(new_fd);
  110. s48_close_channel(new_c_fd);
  111. RETRY_OR_RAISE_NEG(status, dup2(old_c_fd, new_c_fd));
  112. s48_status = s48_set_channel_os_index(channel, new_c_fd);
  113. if (s48_status != S48_TRUE) {
  114. ps_close_fd(new_c_fd); /* retries if interrupted */
  115. s48_raise_scheme_exception(s48_extract_fixnum(s48_status), 1, channel); }
  116. new_channel = s48_add_channel(S48_UNSAFE_CHANNEL_STATUS(channel),
  117. S48_UNSAFE_CHANNEL_ID(channel),
  118. old_c_fd);
  119. if (!S48_CHANNEL_P(new_channel)) {
  120. ps_close_fd(old_c_fd); /* retries if interrupted */
  121. s48_raise_scheme_exception(s48_extract_fixnum(new_channel), 1, channel); }
  122. return new_channel;
  123. }
  124. /*
  125. * Opens a pipe and returns a pair (<input-channel> . <output-channel>).
  126. *
  127. * Synopsis:
  128. * int fds[2];
  129. * pipe(fds);
  130. * return s48_cons(s48_add_channel(fds[1]), s48_add_channel(fds[2]));
  131. */
  132. static s48_value
  133. posix_pipe()
  134. {
  135. int fildes[2],
  136. status;
  137. s48_value in_channel = S48_FALSE,
  138. out_channel = S48_FALSE;
  139. s48_value id = s48_enter_string_latin_1("pipe");
  140. S48_DECLARE_GC_PROTECT(3);
  141. S48_GC_PROTECT_3(in_channel, out_channel, id);
  142. RETRY_OR_RAISE_NEG(status, pipe(fildes));
  143. in_channel = s48_add_channel(S48_CHANNEL_STATUS_INPUT, id, fildes[0]);
  144. if (!S48_CHANNEL_P(in_channel)) {
  145. ps_close_fd(fildes[0]); /* retries if interrupted */
  146. ps_close_fd(fildes[1]); /* retries if interrupted */
  147. s48_raise_scheme_exception(s48_extract_fixnum(in_channel), 0); }
  148. RETRY_OR_RAISE_NEG(status, fcntl(fildes[1], F_SETFL, O_NONBLOCK));
  149. out_channel = s48_add_channel(S48_CHANNEL_STATUS_OUTPUT, id, fildes[1]);
  150. if (!S48_CHANNEL_P(out_channel)) {
  151. s48_close_channel(fildes[0]);
  152. ps_close_fd(fildes[1]); /* retries if interrupted */
  153. s48_raise_scheme_exception(s48_extract_fixnum(in_channel), 0); }
  154. S48_GC_UNPROTECT();
  155. return s48_cons(in_channel, out_channel);
  156. }
  157. static s48_value
  158. posix_close_on_exec_p(s48_value channel)
  159. {
  160. int c_fd,
  161. status;
  162. if (!S48_CHANNEL_P(channel) ||
  163. S48_CHANNEL_STATUS(channel) == S48_CHANNEL_STATUS_CLOSED)
  164. s48_raise_argument_type_error(channel);
  165. c_fd = S48_UNSAFE_EXTRACT_FIXNUM(S48_UNSAFE_CHANNEL_OS_INDEX(channel));
  166. RETRY_OR_RAISE_NEG(status, fcntl(c_fd, F_GETFD));
  167. return S48_ENTER_BOOLEAN(status & FD_CLOEXEC);
  168. }
  169. static s48_value
  170. posix_set_close_on_exec(s48_value channel, s48_value value)
  171. {
  172. int status, new_status;
  173. int c_fd;
  174. if (!S48_CHANNEL_P(channel) ||
  175. S48_CHANNEL_STATUS(channel) == S48_CHANNEL_STATUS_CLOSED)
  176. s48_raise_argument_type_error(channel);
  177. c_fd = S48_UNSAFE_EXTRACT_FIXNUM(S48_UNSAFE_CHANNEL_OS_INDEX(channel));
  178. RETRY_OR_RAISE_NEG(status, fcntl(c_fd, F_GETFD));
  179. if (S48_EXTRACT_BOOLEAN(value))
  180. new_status = status | FD_CLOEXEC;
  181. else
  182. new_status = status & ! FD_CLOEXEC;
  183. if (new_status != status)
  184. RETRY_OR_RAISE_NEG(status, fcntl(c_fd, F_SETFD, new_status));
  185. return S48_UNSPECIFIC;
  186. }
  187. static s48_value
  188. posix_io_flags(s48_value channel, s48_value options)
  189. {
  190. int status;
  191. int c_fd;
  192. if (!S48_CHANNEL_P(channel) ||
  193. S48_CHANNEL_STATUS(channel) == S48_CHANNEL_STATUS_CLOSED)
  194. s48_raise_argument_type_error(channel);
  195. c_fd = S48_UNSAFE_EXTRACT_FIXNUM(S48_UNSAFE_CHANNEL_OS_INDEX(channel));
  196. if (options == S48_FALSE) {
  197. RETRY_OR_RAISE_NEG(status, fcntl(c_fd, F_GETFL));
  198. return s48_enter_file_options(status);
  199. }
  200. else {
  201. int c_options = s48_extract_file_options(options);
  202. RETRY_OR_RAISE_NEG(status, fcntl(c_fd, F_SETFL, c_options));
  203. return S48_UNSPECIFIC;
  204. }
  205. }
  206. /* ************************************************************ */
  207. /* File options.
  208. *
  209. * We translate the local bits into our own bits and vice versa.
  210. */
  211. s48_value
  212. s48_enter_file_options(int file_options)
  213. {
  214. s48_value sch_file_options;
  215. int my_file_options;
  216. my_file_options =
  217. (O_CREAT & file_options ? 00001 : 0) |
  218. (O_EXCL & file_options ? 00002 : 0) |
  219. (O_NOCTTY & file_options ? 00004 : 0) |
  220. (O_TRUNC & file_options ? 00010 : 0) |
  221. (O_APPEND & file_options ? 00020 : 0) |
  222. /* POSIX 2nd ed., not in Linux
  223. (O_DSYNC & file_options ? 00040 : 0) |
  224. */
  225. (O_NONBLOCK & file_options ? 00100 : 0) |
  226. /* POSIX 2nd ed., not in Linux
  227. (O_RSYNC & file_options ? 00200 : 0) |
  228. */
  229. /* Not in FreeBSD
  230. (O_SYNC & file_options ? 00400 : 0) |
  231. */
  232. (O_RDONLY & file_options ? 01000 : 0) |
  233. (O_RDWR & file_options ? 02000 : 0) |
  234. (O_WRONLY & file_options ? 04000 : 0);
  235. sch_file_options
  236. = s48_integer2enum_set(posix_file_options_enum_set_type_binding,
  237. my_file_options);
  238. return sch_file_options;
  239. }
  240. int
  241. s48_extract_file_options(s48_value sch_file_options)
  242. {
  243. int c_file_options;
  244. long file_options;
  245. s48_check_enum_set_type(sch_file_options,
  246. posix_file_options_enum_set_type_binding);
  247. file_options = s48_enum_set2integer(sch_file_options);
  248. c_file_options =
  249. (00001 & file_options ? O_CREAT : 0) |
  250. (00002 & file_options ? O_EXCL : 0) |
  251. (00004 & file_options ? O_NOCTTY : 0) |
  252. (00010 & file_options ? O_TRUNC : 0) |
  253. (00020 & file_options ? O_APPEND : 0) |
  254. /* POSIX 2nd ed., not in Linux
  255. (00040 & file_options ? O_DSYNC : 0) |
  256. */
  257. (00100 & file_options ? O_NONBLOCK : 0) |
  258. /* POSIX 2nd ed., not in Linux
  259. (00200 & file_options ? O_RSYNC : 0) |
  260. */
  261. /* Not in FreeBSD
  262. (00400 & file_options ? O_SYNC : 0) |
  263. */
  264. (01000 & file_options ? O_RDONLY : 0) |
  265. (02000 & file_options ? O_RDWR : 0) |
  266. (04000 & file_options ? O_WRONLY : 0);
  267. return c_file_options;
  268. }