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