devstream.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /* stdio on a Mach device port.
  2. Translates \n to \r\n on output, echos and translates \r to \n on input.
  3. Copyright (C) 1992-2026 Free Software Foundation, Inc.
  4. This file is part of the GNU C Library.
  5. The GNU C Library is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU Lesser General Public
  7. License as published by the Free Software Foundation; either
  8. version 2.1 of the License, or (at your option) any later version.
  9. The GNU C Library is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. Lesser General Public License for more details.
  13. You should have received a copy of the GNU Lesser General Public
  14. License along with the GNU C Library; if not, see
  15. <https://www.gnu.org/licenses/>. */
  16. #include <stdio.h>
  17. #include <mach.h>
  18. #include <device/device.h>
  19. #include <errno.h>
  20. #include <string.h>
  21. #include <libioP.h>
  22. static ssize_t
  23. devstream_write (void *cookie, const char *buffer, size_t n)
  24. {
  25. const device_t dev = (device_t) (uintptr_t) cookie;
  26. int write_some (const char *p, size_t to_write)
  27. {
  28. kern_return_t err;
  29. int wrote;
  30. int thiswrite;
  31. while (to_write > 0)
  32. {
  33. thiswrite = to_write;
  34. if (thiswrite > IO_INBAND_MAX)
  35. thiswrite = IO_INBAND_MAX;
  36. if (err = device_write_inband (dev, 0, 0, p, thiswrite, &wrote))
  37. {
  38. errno = err;
  39. return 0;
  40. }
  41. p += wrote;
  42. to_write -= wrote;
  43. }
  44. return 1;
  45. }
  46. int write_crlf (void)
  47. {
  48. static const char crlf[] = "\r\n";
  49. return write_some (crlf, 2);
  50. }
  51. /* Search for newlines (LFs) in the buffer. */
  52. const char *start = buffer, *p;
  53. while ((p = memchr (start, '\n', n)) != NULL)
  54. {
  55. /* Found one. Write out through the preceding character,
  56. and then write a CR/LF pair. */
  57. if ((p > start && !write_some (start, p - start))
  58. || !write_crlf ())
  59. return (start - buffer) ?: -1;
  60. n -= p + 1 - start;
  61. start = p + 1;
  62. }
  63. /* Write the remainder of the buffer. */
  64. if (write_some (start, n))
  65. start += n;
  66. return (start - buffer) ?: -1;
  67. }
  68. static ssize_t
  69. devstream_read (void *cookie, char *buffer, size_t to_read)
  70. {
  71. const device_t dev = (device_t) (uintptr_t) cookie;
  72. kern_return_t err;
  73. mach_msg_type_number_t nread = to_read;
  74. err = device_read_inband (dev, 0, 0, to_read, buffer, &nread);
  75. if (err)
  76. {
  77. errno = err;
  78. return -1;
  79. }
  80. /* Translate CR to LF. */
  81. {
  82. char *p;
  83. for (p = memchr (buffer, '\r', nread); p;
  84. p = memchr (p + 1, '\r', (buffer + nread) - (p + 1)))
  85. *p = '\n';
  86. }
  87. /* Echo back what we read. */
  88. (void) devstream_write (cookie, buffer, nread);
  89. return nread;
  90. }
  91. static int
  92. dealloc_ref (void *cookie)
  93. {
  94. const device_t dev = (device_t) (uintptr_t) cookie;
  95. if (__mach_port_deallocate (mach_task_self (), dev))
  96. {
  97. errno = EINVAL;
  98. return -1;
  99. }
  100. return 0;
  101. }
  102. FILE *
  103. mach_open_devstream (mach_port_t dev, const char *mode)
  104. {
  105. FILE *stream;
  106. if (mach_port_mod_refs (mach_task_self (), dev, MACH_PORT_RIGHT_SEND, 1))
  107. {
  108. errno = EINVAL;
  109. return NULL;
  110. }
  111. stream = _IO_fopencookie ((void *) (uintptr_t) dev, mode,
  112. (cookie_io_functions_t) { write: devstream_write,
  113. read: devstream_read,
  114. close: dealloc_ref });
  115. if (stream == NULL)
  116. {
  117. __mach_port_deallocate (mach_task_self (), dev);
  118. return NULL;
  119. }
  120. return stream;
  121. }