NPTL is no longer an add-on!
[platform/upstream/glibc.git] / mach / devstream.c
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-2014 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 }