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