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