1 /* $NetBSD: tilde.c,v 1.1.1.1 2016/01/14 00:11:29 christos Exp $ */
2
3 /* tilde.c -- tilde expansion code (~/foo := $HOME/foo).
4 Id: tilde.c,v 1.3 2004/04/11 17:56:46 karl Exp
5
6 Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1996, 1998, 1999,
7 2002, 2004 Free Software Foundation, Inc.
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23 Written by Brian Fox (bfox@ai.mit.edu). */
24
25 /* Include config.h before doing alloca. */
26 #include "info.h"
27 #include "tilde.h"
28
29 #if defined (TEST) || defined (STATIC_MALLOC)
30 static void *xmalloc (), *xrealloc ();
31 #endif /* TEST || STATIC_MALLOC */
32
33 /* The default value of tilde_additional_prefixes. This is set to
34 whitespace preceding a tilde so that simple programs which do not
35 perform any word separation get desired behaviour. */
36 static char *default_prefixes[] =
37 { " ~", "\t~", (char *)NULL };
38
39 /* The default value of tilde_additional_suffixes. This is set to
40 whitespace or newline so that simple programs which do not
41 perform any word separation get desired behaviour. */
42 static char *default_suffixes[] =
43 { " ", "\n", (char *)NULL };
44
45 /* If non-null, this contains the address of a function to call if the
46 standard meaning for expanding a tilde fails. The function is called
47 with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
48 which is the expansion, or a NULL pointer if there is no expansion. */
49 CFunction *tilde_expansion_failure_hook = (CFunction *)NULL;
50
51 /* When non-null, this is a NULL terminated array of strings which
52 are duplicates for a tilde prefix. Bash uses this to expand
53 `=~' and `:~'. */
54 char **tilde_additional_prefixes = default_prefixes;
55
56 /* When non-null, this is a NULL terminated array of strings which match
57 the end of a username, instead of just "/". Bash sets this to
58 `:' and `=~'. */
59 char **tilde_additional_suffixes = default_suffixes;
60
61 /* Find the start of a tilde expansion in STRING, and return the index of
62 the tilde which starts the expansion. Place the length of the text
63 which identified this tilde starter in LEN, excluding the tilde itself. */
64 static int
tilde_find_prefix(char * string,int * len)65 tilde_find_prefix (char *string, int *len)
66 {
67 register int i, j, string_len;
68 register char **prefixes = tilde_additional_prefixes;
69
70 string_len = strlen (string);
71 *len = 0;
72
73 if (!*string || *string == '~')
74 return (0);
75
76 if (prefixes)
77 {
78 for (i = 0; i < string_len; i++)
79 {
80 for (j = 0; prefixes[j]; j++)
81 {
82 if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
83 {
84 *len = strlen (prefixes[j]) - 1;
85 return (i + *len);
86 }
87 }
88 }
89 }
90 return (string_len);
91 }
92
93 /* Find the end of a tilde expansion in STRING, and return the index of
94 the character which ends the tilde definition. */
95 static int
tilde_find_suffix(char * string)96 tilde_find_suffix (char *string)
97 {
98 register int i, j, string_len;
99 register char **suffixes = tilde_additional_suffixes;
100
101 string_len = strlen (string);
102
103 for (i = 0; i < string_len; i++)
104 {
105 if (IS_SLASH (string[i]) || !string[i])
106 break;
107
108 for (j = 0; suffixes && suffixes[j]; j++)
109 {
110 if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
111 return (i);
112 }
113 }
114 return (i);
115 }
116
117 /* Return a new string which is the result of tilde expanding STRING. */
118 char *
tilde_expand(char * string)119 tilde_expand (char *string)
120 {
121 char *result;
122 int result_size, result_index;
123
124 result_size = result_index = 0;
125 result = (char *)NULL;
126
127 /* Scan through STRING expanding tildes as we come to them. */
128 while (1)
129 {
130 register int start, end;
131 char *tilde_word, *expansion;
132 int len;
133
134 /* Make START point to the tilde which starts the expansion. */
135 start = tilde_find_prefix (string, &len);
136
137 /* Copy the skipped text into the result. */
138 if ((result_index + start + 1) > result_size)
139 result = (char *)xrealloc (result, 1 + (result_size += (start + 20)));
140
141 strncpy (result + result_index, string, start);
142 result_index += start;
143
144 /* Advance STRING to the starting tilde. */
145 string += start;
146
147 /* Make END be the index of one after the last character of the
148 username. */
149 end = tilde_find_suffix (string);
150
151 /* If both START and END are zero, we are all done. */
152 if (!start && !end)
153 break;
154
155 /* Expand the entire tilde word, and copy it into RESULT. */
156 tilde_word = (char *)xmalloc (1 + end);
157 strncpy (tilde_word, string, end);
158 tilde_word[end] = '\0';
159 string += end;
160
161 expansion = tilde_expand_word (tilde_word);
162 free (tilde_word);
163
164 len = strlen (expansion);
165 if ((result_index + len + 1) > result_size)
166 result = (char *)xrealloc (result, 1 + (result_size += (len + 20)));
167
168 strcpy (result + result_index, expansion);
169 result_index += len;
170 free (expansion);
171 }
172
173 result[result_index] = '\0';
174
175 return (result);
176 }
177
178 /* Do the work of tilde expansion on FILENAME. FILENAME starts with a
179 tilde. If there is no expansion, call tilde_expansion_failure_hook. */
180 char *
tilde_expand_word(char * filename)181 tilde_expand_word (char *filename)
182 {
183 char *dirname = filename ? xstrdup (filename) : NULL;
184
185 if (dirname && *dirname == '~')
186 {
187 char *temp_name;
188 if (!dirname[1] || IS_SLASH (dirname[1]))
189 {
190 /* Prepend $HOME to the rest of the string. */
191 char *temp_home = getenv ("HOME");
192
193 /* If there is no HOME variable, look up the directory in
194 the password database. */
195 if (!temp_home)
196 {
197 struct passwd *entry;
198
199 entry = (struct passwd *) getpwuid (getuid ());
200 if (entry)
201 temp_home = entry->pw_dir;
202 }
203
204 temp_name = xmalloc (1 + strlen (&dirname[1])
205 + (temp_home ? strlen (temp_home) : 0));
206 if (temp_home)
207 strcpy (temp_name, temp_home);
208 else
209 temp_name[0] = 0;
210 strcat (temp_name, &dirname[1]);
211 free (dirname);
212 dirname = xstrdup (temp_name);
213 free (temp_name);
214 }
215 else
216 {
217 struct passwd *user_entry;
218 char *username = xmalloc (257);
219 int i, c;
220
221 for (i = 1; (c = dirname[i]); i++)
222 {
223 if (IS_SLASH (c))
224 break;
225 else
226 username[i - 1] = c;
227 }
228 username[i - 1] = 0;
229
230 if (!(user_entry = (struct passwd *) getpwnam (username)))
231 {
232 /* If the calling program has a special syntax for
233 expanding tildes, and we couldn't find a standard
234 expansion, then let them try. */
235 if (tilde_expansion_failure_hook)
236 {
237 char *expansion = (*tilde_expansion_failure_hook) (username);
238
239 if (expansion)
240 {
241 temp_name = xmalloc (1 + strlen (expansion)
242 + strlen (&dirname[i]));
243 strcpy (temp_name, expansion);
244 strcat (temp_name, &dirname[i]);
245 free (expansion);
246 goto return_name;
247 }
248 }
249 /* We shouldn't report errors. */
250 }
251 else
252 {
253 temp_name = xmalloc (1 + strlen (user_entry->pw_dir)
254 + strlen (&dirname[i]));
255 strcpy (temp_name, user_entry->pw_dir);
256 strcat (temp_name, &dirname[i]);
257
258 return_name:
259 free (dirname);
260 dirname = xstrdup (temp_name);
261 free (temp_name);
262 }
263
264 endpwent ();
265 free (username);
266 }
267 }
268 return dirname;
269 }
270
271
272 #if defined (TEST)
273 #undef NULL
274 #include <stdio.h>
275
main(argc,argv)276 main (argc, argv)
277 int argc;
278 char **argv;
279 {
280 char *result, line[512];
281 int done = 0;
282
283 while (!done)
284 {
285 printf ("~expand: ");
286 fflush (stdout);
287
288 if (!gets (line))
289 strcpy (line, "done");
290
291 if ((strcmp (line, "done") == 0) ||
292 (strcmp (line, "quit") == 0) ||
293 (strcmp (line, "exit") == 0))
294 {
295 done = 1;
296 break;
297 }
298
299 result = tilde_expand (line);
300 printf (" --> %s\n", result);
301 free (result);
302 }
303 xexit (0);
304 }
305
306 static void memory_error_and_abort ();
307
308 static void *
xmalloc(bytes)309 xmalloc (bytes)
310 int bytes;
311 {
312 void *temp = (void *)malloc (bytes);
313
314 if (!temp)
315 memory_error_and_abort ();
316 return (temp);
317 }
318
319 static void *
xrealloc(pointer,bytes)320 xrealloc (pointer, bytes)
321 void *pointer;
322 int bytes;
323 {
324 void *temp;
325
326 if (!pointer)
327 temp = (char *)malloc (bytes);
328 else
329 temp = (char *)realloc (pointer, bytes);
330
331 if (!temp)
332 memory_error_and_abort ();
333
334 return (temp);
335 }
336
337 static void
memory_error_and_abort()338 memory_error_and_abort ()
339 {
340 fprintf (stderr, _("readline: Out of virtual memory!\n"));
341 abort ();
342 }
343 #endif /* TEST */
344
345