1 /* tilde.c: expand user's home directories.
2
3 Copyright 1997, 1998, 2005, Olaf Weber.
4 Copyright 1993, 1995, 1996, 1997, 2008, 2011 Karl Berry.
5
6 This 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 This 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 License
17 along with this library; if not, see <http://www.gnu.org/licenses/>. */
18
19 #include <kpathsea/config.h>
20
21 #include <kpathsea/c-pathch.h>
22 #include <kpathsea/tilde.h>
23
24 #undef USE_GETPWNAM
25 #ifdef HAVE_PWD_H
26 #include <pwd.h>
27 #define USE_GETPWNAM 1
28 #elif defined (WIN32) && !defined (__MINGW32__)
29 #define USE_GETPWNAM 1
30 #endif
31
32 #ifdef WIN32
33 #define HOMEVAR "USERPROFILE"
34 #else
35 #define HOMEVAR "HOME"
36 #endif
37
38 /* If NAME has a leading ~ or ~user, Unix-style, expand it to the user's
39 home directory, and return a new malloced string. If no ~, or no
40 <pwd.h>, just return NAME. */
41
42 string
kpathsea_tilde_expand(kpathsea kpse,string name)43 kpathsea_tilde_expand (kpathsea kpse, string name)
44 {
45 string expansion;
46 const_string home;
47 const_string prefix;
48 #if defined(WIN32)
49 string p;
50 #endif
51
52 (void)kpse; /* currenty not used */
53 assert (name);
54
55 /* If there is a leading "!!", set prefix to "!!", otherwise use
56 the empty string. After this, we can test whether a prefix was
57 found by checking *prefix, and it is safe to unconditionally
58 prepend it. */
59 if (name[0] == '!' && name[1] == '!') {
60 name += 2;
61 prefix = "!!";
62 } else {
63 prefix = "";
64 }
65
66 /* If no leading tilde, do nothing, and return the original string. */
67 if (*name != '~'
68 #ifndef USE_GETPWNAM
69 /* Same for `~user' or `~user/', but no passwd database. */
70 || (name[1] && !IS_DIR_SEP (name[1]))
71 #endif
72 ) {
73 if (*prefix)
74 name -= 2;
75 expansion = name;
76
77 } else {
78 /* If a bare tilde, return the home directory or `.'; if just `~user',
79 return that user's home directory or `.'. Very unlikely that the
80 directory name will do anyone any good, but ... */
81 unsigned c;
82
83 #ifdef USE_GETPWNAM
84 /* If `~user' or `~user/', look up user in the passwd database. */
85 if (name[1] && !IS_DIR_SEP (name[1])) {
86 struct passwd *p;
87 string user;
88 c = 2;
89 while (!IS_DIR_SEP (name[c]) && name[c] != 0) { /* find user name */
90 #if defined(WIN32)
91 if (IS_KANJI(name+c))
92 c++;
93 #endif
94 c++;
95 }
96
97 user = (string) xmalloc (c);
98 strncpy (user, name + 1, c - 1);
99 user[c - 1] = 0;
100
101 #if defined(WIN32) && !defined(__MINGW32__)
102 p = kpathsea_getpwnam (kpse, user);
103 #else
104 /* We only need the cast here for (deficient) systems
105 which do not declare `getpwnam' in <pwd.h>. */
106 p = (struct passwd *) getpwnam (user);
107 #endif
108 free (user);
109
110 /* If no such user, just use `.'. */
111 home = p ? p->pw_dir : ".";
112 } else
113 #endif /* USE_GETPWNAM */
114 {
115 c = 1;
116 home = getenv (HOMEVAR);
117 if (!home)
118 home = ".";
119 }
120
121 /* handle leading // */
122 if (IS_DIR_SEP (*home) && IS_DIR_SEP (home[1]))
123 home++;
124
125 /* If HOME ends in /, omit the / in ~/ or ~user/. */
126 if (name[c]) {
127 #if defined(WIN32)
128 const_string q;
129
130 for (q = home; *q; q++) {
131 if (IS_DIR_SEP (*q) && q[1] == 0)
132 c++;
133 else if (IS_KANJI(q))
134 q++;
135 }
136 #else
137 if (IS_DIR_SEP (home[strlen (home) - 1]))
138 c++;
139 #endif
140 }
141
142 expansion = concat3 (prefix, home, name + c);
143 }
144
145 #if defined(WIN32)
146 for (p = expansion; *p; p++) {
147 if (*p == '\\')
148 *p = '/';
149 else if (IS_KANJI(p))
150 p++;
151 }
152 #endif
153
154 /* We may return the same thing as the original, and then we might not
155 be returning a malloc-ed string. Callers beware. Sorry. */
156 return expansion;
157 }
158
159 #ifdef TEST
160
161 void
test_expand_tilde(const_string filename)162 test_expand_tilde (const_string filename)
163 {
164 string answer;
165
166 printf ("Tilde expansion of `%s':\t", filename ? filename : "(nil)");
167 answer = kpathsea_tilde_expand (kpse_def, (string)filename);
168 puts (answer);
169 }
170
171 int
main(int argc,char ** argv)172 main (int argc, char **argv)
173 {
174 string tilde_path = "tilde";
175 kpse_set_program_name(argv[0],NULL);
176 test_expand_tilde ("");
177 test_expand_tilde ("none");
178 test_expand_tilde ("~root");
179 test_expand_tilde ("~");
180 test_expand_tilde ("foo~bar");
181
182 test_expand_tilde ("!!");
183 test_expand_tilde ("!!none");
184 test_expand_tilde ("!!~root");
185 test_expand_tilde ("!!~");
186 test_expand_tilde ("!!foo~bar");
187
188 return 0;
189 }
190
191 #endif /* TEST */
192
193
194 /*
195 Local variables:
196 standalone-compile-command: "gcc -g -I. -I.. -DTEST tilde.c kpathsea.a"
197 End:
198 */
199