ioext.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. /* Copyright 1995-2001,2003,2006,2011,2014,2018
  2. Free Software Foundation, Inc.
  3. This file is part of Guile.
  4. Guile is free software: you can redistribute it and/or modify it
  5. under the terms of the GNU Lesser General Public License as published
  6. by the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. Guile is distributed in the hope that it will be useful, but WITHOUT
  9. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10. FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  11. License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with Guile. If not, see
  14. <https://www.gnu.org/licenses/>. */
  15. #ifdef HAVE_CONFIG_H
  16. # include <config.h>
  17. #endif
  18. #include <errno.h>
  19. #include <fcntl.h>
  20. #include <stdio.h>
  21. #include <unistd.h>
  22. #ifdef HAVE_IO_H
  23. #include <io.h>
  24. #endif
  25. #include "async.h"
  26. #include "dynwind.h"
  27. #include "extensions.h"
  28. #include "fdes-finalizers.h"
  29. #include "feature.h"
  30. #include "fports.h"
  31. #include "gsubr.h"
  32. #include "hashtab.h"
  33. #include "numbers.h"
  34. #include "pairs.h"
  35. #include "ports-internal.h"
  36. #include "ports.h"
  37. #include "strings.h"
  38. #include "syscalls.h"
  39. #include "weak-set.h"
  40. #include "version.h"
  41. #include "ioext.h"
  42. SCM_DEFINE (scm_ftell, "ftell", 1, 0, 0,
  43. (SCM fd_port),
  44. "Return an integer representing the current position of\n"
  45. "@var{fd_port}, measured from the beginning. Equivalent to:\n"
  46. "\n"
  47. "@lisp\n"
  48. "(seek port 0 SEEK_CUR)\n"
  49. "@end lisp")
  50. #define FUNC_NAME s_scm_ftell
  51. {
  52. return scm_seek (fd_port, SCM_INUM0, scm_from_int (SEEK_CUR));
  53. }
  54. #undef FUNC_NAME
  55. SCM_DEFINE (scm_redirect_port, "redirect-port", 2, 0, 0,
  56. (SCM old, SCM new),
  57. "This procedure takes two ports and duplicates the underlying file\n"
  58. "descriptor from @var{old} into @var{new}. The\n"
  59. "current file descriptor in @var{new} will be closed.\n"
  60. "After the redirection the two ports will share a file position\n"
  61. "and file status flags.\n\n"
  62. "The return value is unspecified.\n\n"
  63. "Unexpected behaviour can result if both ports are subsequently used\n"
  64. "and the original and/or duplicate ports are buffered.\n\n"
  65. "This procedure does not have any side effects on other ports or\n"
  66. "revealed counts.")
  67. #define FUNC_NAME s_scm_redirect_port
  68. {
  69. int ans, oldfd, newfd;
  70. scm_t_fport *fp;
  71. old = SCM_COERCE_OUTPORT (old);
  72. new = SCM_COERCE_OUTPORT (new);
  73. SCM_VALIDATE_OPFPORT (1, old);
  74. SCM_VALIDATE_OPFPORT (2, new);
  75. oldfd = SCM_FPORT_FDES (old);
  76. fp = SCM_FSTREAM (new);
  77. newfd = fp->fdes;
  78. if (oldfd != newfd)
  79. {
  80. /* Ensure there is nothing in either port's input or output
  81. buffers. */
  82. if (SCM_OUTPUT_PORT_P (old))
  83. scm_flush (old);
  84. if (SCM_INPUT_PORT_P (old) && SCM_PORT (old)->rw_random)
  85. scm_end_input (old);
  86. if (SCM_OUTPUT_PORT_P (new))
  87. scm_flush (new);
  88. if (SCM_INPUT_PORT_P (new) && SCM_PORT (new)->rw_random)
  89. scm_end_input (new);
  90. ans = dup2 (oldfd, newfd);
  91. if (ans == -1)
  92. SCM_SYSERROR;
  93. SCM_PORT (new)->rw_random = SCM_PORT (old)->rw_random;
  94. }
  95. return SCM_UNSPECIFIED;
  96. }
  97. #undef FUNC_NAME
  98. SCM_DEFINE (scm_dup_to_fdes, "dup->fdes", 1, 1, 0,
  99. (SCM fd_or_port, SCM fd),
  100. "Return a new integer file descriptor referring to the open file\n"
  101. "designated by @var{fd_or_port}, which must be either an open\n"
  102. "file port or a file descriptor.")
  103. #define FUNC_NAME s_scm_dup_to_fdes
  104. {
  105. int oldfd, newfd, rv;
  106. fd_or_port = SCM_COERCE_OUTPORT (fd_or_port);
  107. if (scm_is_integer (fd_or_port))
  108. oldfd = scm_to_int (fd_or_port);
  109. else
  110. {
  111. SCM_VALIDATE_OPFPORT (1, fd_or_port);
  112. oldfd = SCM_FPORT_FDES (fd_or_port);
  113. }
  114. if (SCM_UNBNDP (fd))
  115. {
  116. newfd = dup (oldfd);
  117. if (newfd == -1)
  118. SCM_SYSERROR;
  119. fd = scm_from_int (newfd);
  120. }
  121. else
  122. {
  123. newfd = scm_to_int (fd);
  124. if (oldfd != newfd)
  125. {
  126. scm_evict_ports (newfd); /* see scsh manual. */
  127. rv = dup2 (oldfd, newfd);
  128. if (rv == -1)
  129. SCM_SYSERROR;
  130. }
  131. }
  132. return fd;
  133. }
  134. #undef FUNC_NAME
  135. SCM_DEFINE (scm_dup2, "dup2", 2, 0, 0,
  136. (SCM oldfd, SCM newfd),
  137. "A simple wrapper for the @code{dup2} system call.\n"
  138. "Copies the file descriptor @var{oldfd} to descriptor\n"
  139. "number @var{newfd}, replacing the previous meaning\n"
  140. "of @var{newfd}. Both @var{oldfd} and @var{newfd} must\n"
  141. "be integers.\n"
  142. "Unlike for dup->fdes or primitive-move->fdes, no attempt\n"
  143. "is made to move away ports which are using @var{newfd}.\n"
  144. "The return value is unspecified.")
  145. #define FUNC_NAME s_scm_dup2
  146. {
  147. int c_oldfd;
  148. int c_newfd;
  149. int rv;
  150. c_oldfd = scm_to_int (oldfd);
  151. c_newfd = scm_to_int (newfd);
  152. rv = dup2 (c_oldfd, c_newfd);
  153. if (rv == -1)
  154. SCM_SYSERROR;
  155. return SCM_UNSPECIFIED;
  156. }
  157. #undef FUNC_NAME
  158. SCM_DEFINE (scm_fileno, "fileno", 1, 0, 0,
  159. (SCM port),
  160. "Return the integer file descriptor underlying @var{port}. Does\n"
  161. "not change its revealed count.")
  162. #define FUNC_NAME s_scm_fileno
  163. {
  164. port = SCM_COERCE_OUTPORT (port);
  165. SCM_VALIDATE_OPFPORT (1, port);
  166. return scm_from_int (SCM_FPORT_FDES (port));
  167. }
  168. #undef FUNC_NAME
  169. /* GJB:FIXME:: why does this not throw
  170. an error if the arg is not a port?
  171. This proc as is would be better names isattyport?
  172. if it is not going to assume that the arg is a port
  173. [cmm] I don't see any problem with the above. why should a type
  174. predicate assume _anything_ about its argument?
  175. */
  176. SCM_DEFINE (scm_isatty_p, "isatty?", 1, 0, 0,
  177. (SCM port),
  178. "Return @code{#t} if @var{port} is using a serial non--file\n"
  179. "device, otherwise @code{#f}.")
  180. #define FUNC_NAME s_scm_isatty_p
  181. {
  182. int rv;
  183. port = SCM_COERCE_OUTPORT (port);
  184. if (!SCM_OPFPORTP (port))
  185. return SCM_BOOL_F;
  186. rv = isatty (SCM_FPORT_FDES (port));
  187. return scm_from_bool(rv);
  188. }
  189. #undef FUNC_NAME
  190. SCM_DEFINE (scm_fdopen, "fdopen", 2, 0, 0,
  191. (SCM fdes, SCM modes),
  192. "Return a new port based on the file descriptor @var{fdes}.\n"
  193. "Modes are given by the string @var{modes}. The revealed count\n"
  194. "of the port is initialized to zero. The modes string is the\n"
  195. "same as that accepted by @ref{File Ports, open-file}.")
  196. #define FUNC_NAME s_scm_fdopen
  197. {
  198. return scm_i_fdes_to_port (scm_to_int (fdes),
  199. scm_i_mode_bits (modes), SCM_BOOL_F,
  200. SCM_FPORT_OPTION_VERIFY);
  201. }
  202. #undef FUNC_NAME
  203. /* Move a port's underlying file descriptor to a given value.
  204. * Returns #f if fdes is already the given value.
  205. * #t if fdes moved.
  206. * MOVE->FDES is implemented in Scheme and calls this primitive.
  207. */
  208. SCM_DEFINE (scm_primitive_move_to_fdes, "primitive-move->fdes", 2, 0, 0,
  209. (SCM port, SCM fd),
  210. "Moves the underlying file descriptor for @var{port} to the integer\n"
  211. "value @var{fd} without changing the revealed count of @var{port}.\n"
  212. "Any other ports already using this descriptor will be automatically\n"
  213. "shifted to new descriptors and their revealed counts reset to zero.\n"
  214. "The return value is @code{#f} if the file descriptor already had the\n"
  215. "required value or @code{#t} if it was moved.")
  216. #define FUNC_NAME s_scm_primitive_move_to_fdes
  217. {
  218. scm_t_fport *stream;
  219. int old_fd;
  220. int new_fd;
  221. int rv;
  222. port = SCM_COERCE_OUTPORT (port);
  223. SCM_VALIDATE_OPFPORT (1, port);
  224. stream = SCM_FSTREAM (port);
  225. old_fd = stream->fdes;
  226. new_fd = scm_to_int (fd);
  227. if (old_fd == new_fd)
  228. {
  229. return SCM_BOOL_F;
  230. }
  231. scm_evict_ports (new_fd);
  232. rv = dup2 (old_fd, new_fd);
  233. if (rv == -1)
  234. SCM_SYSERROR;
  235. stream->fdes = new_fd;
  236. scm_run_fdes_finalizers (old_fd);
  237. SCM_SYSCALL (close (old_fd));
  238. return SCM_BOOL_T;
  239. }
  240. #undef FUNC_NAME
  241. static SCM
  242. get_matching_port (void *closure, SCM port, SCM result)
  243. {
  244. int fd = * (int *) closure;
  245. if (SCM_OPFPORTP (port) && SCM_FSTREAM (port)->fdes == fd)
  246. result = scm_cons (port, result);
  247. return result;
  248. }
  249. /* Return a list of ports using a given file descriptor. */
  250. SCM_DEFINE (scm_fdes_to_ports, "fdes->ports", 1, 0, 0,
  251. (SCM fd),
  252. "Return a list of existing ports which have @var{fd} as an\n"
  253. "underlying file descriptor, without changing their revealed\n"
  254. "counts.")
  255. #define FUNC_NAME s_scm_fdes_to_ports
  256. {
  257. SCM result = SCM_EOL;
  258. int int_fd = scm_to_int (fd);
  259. result = scm_c_weak_set_fold (get_matching_port,
  260. (void*) &int_fd, result,
  261. scm_i_port_weak_set);
  262. return result;
  263. }
  264. #undef FUNC_NAME
  265. static void
  266. scm_init_ice_9_ioext (void)
  267. {
  268. #include "ioext.x"
  269. }
  270. void
  271. scm_init_ioext ()
  272. {
  273. scm_add_feature ("i/o-extensions");
  274. scm_c_register_extension ("libguile-" SCM_EFFECTIVE_VERSION,
  275. "scm_init_ice_9_ioext",
  276. (scm_t_extension_init_func) scm_init_ice_9_ioext,
  277. NULL);
  278. }