acc3c94cc8cff220a790fcc159d8df991fea09c7
[gnulib.git] / tests / test-ptsname_r.c
1 /* Test of ptsname_r(3).
2    Copyright (C) 2010-2013 Free Software Foundation, Inc.
3
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.
8
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.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16
17 #include <config.h>
18
19 #include <stdlib.h>
20
21 #include "signature.h"
22 SIGNATURE_CHECK (ptsname_r, int, (int, char *, size_t));
23
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <signal.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <sys/stat.h>
31
32 #include "same-inode.h"
33
34 #include "macros.h"
35
36 /* Compare two slave names.
37    On some systems, there are hard links in the /dev/ directory.
38    For example, on OSF/1 5.1,
39      /dev/ttyp0 == /dev/pts/0
40      /dev/ttyp9 == /dev/pts/9
41      /dev/ttypa == /dev/pts/10
42      /dev/ttype == /dev/pts/14
43  */
44 static int
45 same_slave (const char *slave_name1, const char *slave_name2)
46 {
47   struct stat statbuf1;
48   struct stat statbuf2;
49
50   return (strcmp (slave_name1, slave_name2) == 0
51           || (stat (slave_name1, &statbuf1) >= 0
52               && stat (slave_name2, &statbuf2) >= 0
53               && SAME_INODE (statbuf1, statbuf2)));
54 }
55
56 static char *
57 null_ptr (void)
58 {
59   return NULL;
60 }
61
62 static void
63 test_errors (int fd, const char *slave)
64 {
65   char buffer[256];
66   size_t len;
67   size_t buflen_max;
68   size_t buflen;
69   int result;
70
71   len = strlen (slave);
72   buflen_max = len + 5;
73   if (buflen_max > sizeof buffer)
74     buflen_max = sizeof buffer;
75   for (buflen = 0; buflen <= buflen_max; buflen++)
76     {
77       memset (buffer, 'X', sizeof buffer);
78       errno = 0;
79       result = ptsname_r (fd, buffer, buflen);
80       if (buflen > len)
81         {
82           ASSERT (result == 0);
83           ASSERT (buffer[0] == '/');
84         }
85       else
86         {
87           ASSERT (result != 0);
88           ASSERT (result == errno);
89           ASSERT (errno == ERANGE);
90           ASSERT (buffer[0] == 'X');
91         }
92     }
93
94   errno = 0;
95   result = ptsname_r (fd, null_ptr (), 0);
96   ASSERT (result != 0);
97   ASSERT (result == errno);
98   ASSERT (errno == EINVAL);
99 }
100
101 int
102 main (void)
103 {
104 #if HAVE_DECL_ALARM
105   /* Declare failure if test takes too long, by using default abort
106      caused by SIGALRM.  */
107   signal (SIGALRM, SIG_DFL);
108   alarm (5);
109 #endif
110
111   {
112     char buffer[256];
113     int result;
114
115     errno = 0;
116     result = ptsname_r (-1, buffer, sizeof buffer);
117     ASSERT (result != 0);
118     ASSERT (result == errno);
119     ASSERT (errno == EBADF || errno == ENOTTY);
120   }
121
122   {
123     int fd;
124     char buffer[256];
125     int result;
126
127     /* Open the controlling tty of the current process.  */
128     fd = open ("/dev/tty", O_RDONLY);
129     if (fd < 0)
130       {
131         fprintf (stderr, "Skipping test: cannot open controlling tty\n");
132         return 77;
133       }
134
135     result = ptsname_r (fd, buffer, sizeof buffer);
136     /* The result is usually NULL, because /dev/tty is a slave, not a
137        master.  */
138     if (result == 0)
139       {
140         ASSERT (memcmp (buffer, "/dev/", 5) == 0);
141       }
142
143     close (fd);
144   }
145
146 #if defined __sun
147   /* Solaris has BSD-style /dev/pty[p-r][0-9a-f] files, but the function
148      ptsname() does not work on them.  */
149   {
150     int fd;
151     char buffer[256];
152     int result;
153
154     /* Open a pty master.  */
155     fd = open ("/dev/ptmx", O_RDWR | O_NOCTTY);
156     if (fd < 0)
157       {
158         fprintf (stderr, "Skipping test: cannot open pseudo-terminal\n");
159         return 77;
160       }
161
162     result = ptsname_r (fd, buffer, sizeof buffer);
163     ASSERT (result == 0);
164     ASSERT (memcmp (buffer, "/dev/pts/", 9) == 0);
165
166     test_errors (fd, buffer);
167
168     close (fd);
169   }
170
171 #elif defined _AIX
172   /* AIX has BSD-style /dev/ptyp[0-9a-f] files, but the modern way to open
173      a pty is to go through /dev/ptc.  */
174   {
175     int fd;
176     char buffer[256];
177     int result;
178
179     /* Open a pty master.  */
180     fd = open ("/dev/ptc", O_RDWR | O_NOCTTY);
181     if (fd < 0)
182       {
183         fprintf (stderr, "Skipping test: cannot open pseudo-terminal\n");
184         return 77;
185       }
186
187     result = ptsname_r (fd, buffer, sizeof buffer);
188     ASSERT (result == 0);
189     ASSERT (memcmp (buffer, "/dev/pts/", 9) == 0);
190
191     test_errors (fd, buffer);
192
193     /* This call hangs on AIX.  */
194     close (fd);
195   }
196
197 #else
198
199   /* Try various master names of Mac OS X: /dev/pty[p-w][0-9a-f]  */
200   {
201     int char1;
202     int char2;
203
204     for (char1 = 'p'; char1 <= 'w'; char1++)
205       for (char2 = '0'; char2 <= 'f'; (char2 == '9' ? char2 = 'a' : char2++))
206         {
207           char master_name[32];
208           int fd;
209
210           sprintf (master_name, "/dev/pty%c%c", char1, char2);
211           fd = open (master_name, O_RDONLY);
212           if (fd >= 0)
213             {
214               char buffer[256];
215               int result;
216               char slave_name[32];
217
218               result = ptsname_r (fd, buffer, sizeof buffer);
219               ASSERT (result == 0);
220               sprintf (slave_name, "/dev/tty%c%c", char1, char2);
221               ASSERT (same_slave (buffer, slave_name));
222
223               test_errors (fd, buffer);
224
225               /* This call hangs on AIX.  */
226               close (fd);
227             }
228         }
229   }
230
231   /* Try various master names of *BSD: /dev/pty[p-sP-S][0-9a-v]  */
232   {
233     int upper;
234     int char1;
235     int char2;
236
237     for (upper = 0; upper <= 1; upper++)
238       for (char1 = (upper ? 'P' : 'p'); char1 <= (upper ? 'S' : 's'); char1++)
239         for (char2 = '0'; char2 <= 'v'; (char2 == '9' ? char2 = 'a' : char2++))
240           {
241             char master_name[32];
242             int fd;
243
244             sprintf (master_name, "/dev/pty%c%c", char1, char2);
245             fd = open (master_name, O_RDONLY);
246             if (fd >= 0)
247               {
248                 char buffer[256];
249                 int result;
250                 char slave_name[32];
251
252                 result = ptsname_r (fd, buffer, sizeof buffer);
253                 ASSERT (result == 0);
254                 sprintf (slave_name, "/dev/tty%c%c", char1, char2);
255                 ASSERT (same_slave (buffer, slave_name));
256
257                 test_errors (fd, buffer);
258
259                 close (fd);
260               }
261           }
262   }
263
264 #endif
265
266   return 0;
267 }