Fix a memory access and a double slash bug.
[gpgme.git] / src / ttyname_r.c
1 /* ttyname_r.c - A ttyname_r() replacement.
2    Copyright (C) 2003, 2004, 2012 g10 Code GmbH
3
4    This file is part of GPGME.
5
6    GPGME is free software; you can redistribute it and/or modify it
7    under the terms of the GNU Lesser General Public License as
8    published by the Free Software Foundation; either version 2.1 of
9    the License, or (at your option) any later version.
10
11    GPGME is distributed in the hope that it will be useful, but
12    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 this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #if HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <string.h>
26 #ifdef HAVE_UNISTD_H
27 # include <unistd.h>
28 #endif
29
30 \f
31 #if !HAVE_TTYNAME_R && defined(__GNUC__)
32 # warning ttyname is not thread-safe, and ttyname_r is missing
33 #endif
34
35 /* For Android we force the use of our replacement code.  */
36 #if HAVE_ANDROID_SYSTEM
37 # undef HAVE_TTYNAME_R
38 #endif
39
40
41 int
42 _gpgme_ttyname_r (int fd, char *buf, size_t buflen)
43 {
44 #if HAVE_TTYNAME_R
45 # if HAVE_BROKEN_TTYNAME_R
46    /* Solaris fails if BUFLEN is less than 128. OSF/1 5.1 completely
47       ignores BUFLEN.  We use a large buffer to woraround this.  */
48   {
49     char largebuf[512];
50     size_t namelen;
51     int rc;
52
53 #  if HAVE_POSIXDECL_TTYNAME_R
54     if (buflen < sizeof (largebuf))
55       {
56         rc = ttyname_r (fd, largebuf, (int)sizeof (largebuf));
57         if (!rc)
58           {
59             namelen = strlen (largebuf) + 1;
60             if (namelen > buflen)
61               rc = ERANGE;
62             else
63               memcpy (buf, largebuf, namelen);
64           }
65       }
66     else
67       rc = ttyname_r (fd, buf, (int)buflen);
68
69 #  else /*!HAVE_POSIXDECL_TTYNAME_R*/
70     char *name;
71
72     if (buflen < sizeof (largebuf))
73       name = ttyname_r (fd, largebuf, (int)sizeof (largebuf));
74     else
75       name = ttyname_r (fd, buf, (int)buflen);
76     rc = name? 0 : (errno? errno : -1);
77     if (!rc && buf != name)
78       {
79         namelen = strlen (name) + 1;
80         if (namelen > buflen)
81           rc = ERANGE;
82         else
83           memmove (buf, name, namelen);
84       }
85 #  endif
86
87     return rc;
88   }
89 # else /*!HAVE_BROKEN_TTYNAME_R*/
90   {
91     int rc;
92
93 #  if HAVE_POSIXDECL_TTYNAME_R
94
95     rc = ttyname_r (fd, buf, buflen);
96
97 #  else /*!HAVE_POSIXDECL_TTYNAME_R*/
98     char *name;
99     size_t namelen;
100
101     name = ttyname_r (fd, buf, (int)buflen);
102     rc = name? 0 : (errno? errno : -1);
103     if (!rc && buf != name)
104       {
105         namelen = strlen (name) + 1;
106         if (namelen > buflen)
107           rc = ERANGE;
108         else
109           memmove (buf, name, namelen);
110       }
111 #  endif
112
113     return rc;
114   }
115 # endif /*!HAVE_BROKEN_TTYNAME_R*/
116 #else /*!HAVE_TTYNAME_R*/
117   char *tty;
118
119 # if HAVE_W32_SYSTEM || HAVE_ANDROID_SYSTEM
120   /* We use this default one for now.  AFAICS we only need it to be
121      passed to gpg and in turn to pinentry.  Providing a replacement
122      is needed because elsewhere we bail out on error or Android
123      provided ttyname_r prints an error message if used. */
124   tty = "/dev/tty";
125 # else
126   tty = ttyname (fd);
127   if (!tty)
128     return errno? errno : -1;
129 # endif
130
131   strncpy (buf, tty, buflen);
132   buf[buflen - 1] = '\0';
133   return (strlen (tty) >= buflen) ? ERANGE : 0;
134 #endif /*!HAVE_TTYNAME_R*/
135 }