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