| /* stdio on a Mach device port. |
| Translates \n to \r\n on output, echos and translates \r to \n on input. |
| Copyright (C) 1992-2014 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| |
| The GNU C Library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| The GNU C Library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with the GNU C Library; if not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <stdio.h> |
| #include <mach.h> |
| #include <device/device.h> |
| #include <errno.h> |
| #include <string.h> |
| |
| |
| static ssize_t |
| devstream_write (void *cookie, const char *buffer, size_t n) |
| { |
| const device_t dev = (device_t) cookie; |
| |
| int write_some (const char *p, size_t to_write) |
| { |
| kern_return_t err; |
| int wrote; |
| int thiswrite; |
| |
| while (to_write > 0) |
| { |
| thiswrite = to_write; |
| if (thiswrite > IO_INBAND_MAX) |
| thiswrite = IO_INBAND_MAX; |
| |
| if (err = device_write_inband (dev, 0, 0, p, thiswrite, &wrote)) |
| { |
| errno = err; |
| return 0; |
| } |
| p += wrote; |
| to_write -= wrote; |
| } |
| return 1; |
| } |
| int write_crlf (void) |
| { |
| static const char crlf[] = "\r\n"; |
| return write_some (crlf, 2); |
| } |
| |
| /* Search for newlines (LFs) in the buffer. */ |
| |
| const char *start = buffer, *p; |
| while ((p = memchr (start, '\n', n)) != NULL) |
| { |
| /* Found one. Write out through the preceding character, |
| and then write a CR/LF pair. */ |
| |
| if ((p > start && !write_some (start, p - start)) |
| || !write_crlf ()) |
| return (start - buffer) ?: -1; |
| |
| n -= p + 1 - start; |
| start = p + 1; |
| } |
| |
| /* Write the remainder of the buffer. */ |
| if (write_some (start, n)) |
| start += n; |
| return (start - buffer) ?: -1; |
| } |
| |
| static ssize_t |
| devstream_read (void *cookie, char *buffer, size_t to_read) |
| { |
| const device_t dev = (device_t) cookie; |
| |
| kern_return_t err; |
| mach_msg_type_number_t nread = to_read; |
| |
| err = device_read_inband (dev, 0, 0, to_read, buffer, &nread); |
| if (err) |
| { |
| errno = err; |
| return -1; |
| } |
| |
| /* Translate CR to LF. */ |
| { |
| char *p; |
| for (p = memchr (buffer, '\r', nread); p; |
| p = memchr (p + 1, '\r', (buffer + nread) - (p + 1))) |
| *p = '\n'; |
| } |
| |
| /* Echo back what we read. */ |
| (void) devstream_write (cookie, buffer, nread); |
| |
| return nread; |
| } |
| |
| static int |
| dealloc_ref (void *cookie) |
| { |
| if (mach_port_deallocate (mach_task_self (), (mach_port_t) cookie)) |
| { |
| errno = EINVAL; |
| return -1; |
| } |
| return 0; |
| } |
| |
| FILE * |
| mach_open_devstream (mach_port_t dev, const char *mode) |
| { |
| FILE *stream; |
| |
| if (mach_port_mod_refs (mach_task_self (), dev, MACH_PORT_RIGHT_SEND, 1)) |
| { |
| errno = EINVAL; |
| return NULL; |
| } |
| |
| stream = fopencookie ((void *) dev, mode, |
| (cookie_io_functions_t) { write: devstream_write, |
| read: devstream_read, |
| close: dealloc_ref }); |
| if (stream == NULL) |
| { |
| mach_port_deallocate (mach_task_self (), dev); |
| return NULL; |
| } |
| |
| return stream; |
| } |