maint.mk: Update system header list for #include syntax checks.
[gnulib.git] / tests / test-ptsname_r.c
blob5e89dfa249bcf3b097bef83f573777d1abecfe47
1 /* Test of ptsname_r(3).
2 Copyright (C) 2010-2024 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program 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
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Don't use __attribute__ __nonnull__ in this compilation unit. Otherwise gcc
18 may "optimize" the null_ptr function, when its result gets passed to a
19 function that has an argument declared as _GL_ARG_NONNULL. */
20 #define _GL_ARG_NONNULL(params)
22 #include <config.h>
24 #include <stdlib.h>
26 #include "signature.h"
27 SIGNATURE_CHECK (ptsname_r, int, (int, char *, size_t));
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <signal.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <sys/stat.h>
37 #include "same-inode.h"
39 #if GNULIB_defined_ptsname_r
40 # include "null-ptr.h"
41 #endif
43 #include "macros.h"
45 /* Compare two slave names.
46 On some systems, there are hard links in the /dev/ directory.
47 For example, on OSF/1 5.1,
48 /dev/ttyp0 == /dev/pts/0
49 /dev/ttyp9 == /dev/pts/9
50 /dev/ttypa == /dev/pts/10
51 /dev/ttype == /dev/pts/14
53 static int
54 same_slave (const char *slave_name1, const char *slave_name2)
56 struct stat statbuf1;
57 struct stat statbuf2;
59 return (strcmp (slave_name1, slave_name2) == 0
60 || (stat (slave_name1, &statbuf1) >= 0
61 && stat (slave_name2, &statbuf2) >= 0
62 && psame_inode (&statbuf1, &statbuf2)));
65 static void
66 test_errors (int fd, const char *slave)
68 char buffer[256];
69 size_t len;
70 size_t buflen_max;
71 size_t buflen;
72 int result;
74 len = strlen (slave);
75 buflen_max = len + 5;
76 if (buflen_max > sizeof buffer)
77 buflen_max = sizeof buffer;
78 for (buflen = 0; buflen <= buflen_max; buflen++)
80 memset (buffer, 'X', sizeof buffer);
81 result = ptsname_r (fd, buffer, buflen);
82 if (buflen > len)
84 ASSERT (result == 0);
85 ASSERT (buffer[0] == '/');
87 else
89 ASSERT (result != 0);
90 ASSERT (result == ERANGE);
91 ASSERT (buffer[0] == 'X');
95 /* This test works only if the ptsname_r implementation comes from gnulib.
96 If it comes from libc, we have no way to prevent gcc from "optimizing"
97 the null_ptr function in invalid ways. See
98 <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93156>. */
99 #if GNULIB_defined_ptsname_r
100 result = ptsname_r (fd, null_ptr (), 0);
101 ASSERT (result != 0);
102 ASSERT (result == EINVAL);
103 #endif
107 main (void)
109 #if HAVE_DECL_ALARM
110 /* Declare failure if test takes too long, by using default abort
111 caused by SIGALRM. */
112 # if defined _AIX
113 int alarm_value = 20;
114 # else
115 int alarm_value = 5;
116 # endif
117 signal (SIGALRM, SIG_DFL);
118 alarm (alarm_value);
119 #endif
122 char buffer[256];
123 int result;
125 result = ptsname_r (-1, buffer, sizeof buffer);
126 ASSERT (result != 0);
127 ASSERT (result == EBADF || result == ENOTTY);
131 int fd;
132 char buffer[256];
133 int result;
135 /* Open the controlling tty of the current process. */
136 fd = open ("/dev/tty", O_RDONLY);
137 if (fd < 0)
139 if (test_exit_status != EXIT_SUCCESS)
140 return test_exit_status;
141 fprintf (stderr, "Skipping test: cannot open controlling tty\n");
142 return 77;
145 result = ptsname_r (fd, buffer, sizeof buffer);
146 /* The result is usually NULL, because /dev/tty is a slave, not a
147 master. */
148 if (result == 0)
150 ASSERT (memcmp (buffer, "/dev/", 5) == 0);
153 close (fd);
156 #if defined __sun || defined __DragonFly__
157 /* Solaris has BSD-style /dev/pty[p-r][0-9a-f] files, but the function
158 ptsname() does not work on them.
159 DragonFly BSD has only /dev/ptmx. */
161 int fd;
162 char buffer[256];
163 int result;
165 /* Open a pty master. */
166 fd = open ("/dev/ptmx", O_RDWR | O_NOCTTY);
167 if (fd < 0)
169 if (test_exit_status != EXIT_SUCCESS)
170 return test_exit_status;
171 fprintf (stderr, "Skipping test: cannot open pseudo-terminal\n");
172 return 77;
175 result = ptsname_r (fd, buffer, sizeof buffer);
176 ASSERT (result == 0);
177 ASSERT (memcmp (buffer, "/dev/pts/", 9) == 0);
179 test_errors (fd, buffer);
181 close (fd);
184 #elif defined _AIX
185 /* AIX has BSD-style /dev/ptyp[0-9a-f] files, and the function ptsname()
186 produces the corresponding /dev/ttyp[0-9a-f] file for each. But opening
187 such a pty causes the process to hang in a state where it does not even
188 react to the SIGALRM signal for N * 15 seconds, where N is the number of
189 opened ptys, either in the close (fd) call or - when this close (fd) call
190 is commented out - at the process exit.
191 So, better don't use these BSD-style ptys. The modern way to open a pty
192 is to go through /dev/ptc. */
194 int fd;
195 char buffer[256];
196 int result;
198 /* Open a pty master. */
199 fd = open ("/dev/ptc", O_RDWR | O_NOCTTY);
200 if (fd < 0)
202 if (test_exit_status != EXIT_SUCCESS)
203 return test_exit_status;
204 fprintf (stderr, "Skipping test: cannot open pseudo-terminal\n");
205 return 77;
208 result = ptsname_r (fd, buffer, sizeof buffer);
209 ASSERT (result == 0);
210 ASSERT (memcmp (buffer, "/dev/pts/", 9) == 0);
212 test_errors (fd, buffer);
214 /* This close (fd) call takes 15 seconds. It would be interruptible by the
215 SIGALRM timer, but then this test would report failure. */
216 close (fd);
219 #elif defined __GNU__ /* Hurd */
221 /* Try various master names of Hurd: /dev/pty[p-q][0-9a-v] */
223 int char1;
224 int char2;
226 for (char1 = 'p'; char1 <= 'q'; char1++)
227 for (char2 = '0'; char2 <= 'v'; (char2 == '9' ? char2 = 'a' : char2++))
229 char master_name[32];
230 int fd;
232 sprintf (master_name, "/dev/pty%c%c", char1, char2);
233 fd = open (master_name, O_RDONLY);
234 if (fd >= 0)
236 char buffer[256];
237 int result;
238 char slave_name[32];
240 result = ptsname_r (fd, buffer, sizeof buffer);
241 ASSERT (result == 0);
242 sprintf (slave_name, "/dev/tty%c%c", char1, char2);
243 ASSERT (same_slave (buffer, slave_name));
245 test_errors (fd, buffer);
247 close (fd);
252 #else
254 /* Try various master names of Mac OS X: /dev/pty[p-w][0-9a-f] */
256 int char1;
257 int char2;
259 for (char1 = 'p'; char1 <= 'w'; char1++)
260 for (char2 = '0'; char2 <= 'f'; (char2 == '9' ? char2 = 'a' : char2++))
262 char master_name[32];
263 int fd;
265 sprintf (master_name, "/dev/pty%c%c", char1, char2);
266 fd = open (master_name, O_RDONLY);
267 if (fd >= 0)
269 char buffer[256];
270 int result;
271 char slave_name[32];
273 result = ptsname_r (fd, buffer, sizeof buffer);
274 ASSERT (result == 0);
275 sprintf (slave_name, "/dev/tty%c%c", char1, char2);
276 ASSERT (same_slave (buffer, slave_name));
278 test_errors (fd, buffer);
280 /* This call hangs on AIX. */
281 close (fd);
286 /* Try various master names of *BSD: /dev/pty[p-sP-S][0-9a-v] */
288 int upper;
289 int char1;
290 int char2;
292 for (upper = 0; upper <= 1; upper++)
293 for (char1 = (upper ? 'P' : 'p'); char1 <= (upper ? 'S' : 's'); char1++)
294 for (char2 = '0'; char2 <= 'v'; (char2 == '9' ? char2 = 'a' : char2++))
296 char master_name[32];
297 int fd;
299 sprintf (master_name, "/dev/pty%c%c", char1, char2);
300 fd = open (master_name, O_RDONLY);
301 if (fd >= 0)
303 char buffer[256];
304 int result;
305 char slave_name[32];
307 result = ptsname_r (fd, buffer, sizeof buffer);
308 ASSERT (result == 0);
309 sprintf (slave_name, "/dev/tty%c%c", char1, char2);
310 ASSERT (same_slave (buffer, slave_name));
312 test_errors (fd, buffer);
314 close (fd);
319 #endif
321 return test_exit_status;