Document the recently added WITHOUT_SRCS variable.
[dragonfly.git] / contrib / libreadline / 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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
21
22 #if defined (HAVE_CONFIG_H)
23 #  include <config.h>
24 #endif
25
26 #if defined (HAVE_UNISTD_H)
27 #  ifdef _MINIX
28 #    include <sys/types.h>
29 #  endif
30 #  include <unistd.h>
31 #endif
32
33 #if defined (HAVE_STRING_H)
34 #  include <string.h>
35 #else /* !HAVE_STRING_H */
36 #  include <strings.h>
37 #endif /* !HAVE_STRING_H */  
38
39 #if defined (HAVE_STDLIB_H)
40 #  include <stdlib.h>
41 #else
42 #  include "ansi_stdlib.h"
43 #endif /* HAVE_STDLIB_H */
44
45 #include <sys/types.h>
46 #include <pwd.h>
47
48 #include "tilde.h"
49
50 #if defined (TEST) || defined (STATIC_MALLOC)
51 static char *xmalloc (), *xrealloc ();
52 #else
53 #  if defined __STDC__
54 extern char *xmalloc (int);
55 extern char *xrealloc (void *, int);
56 #  else
57 extern char *xmalloc (), *xrealloc ();
58 #  endif /* !__STDC__ */
59 #endif /* TEST || STATIC_MALLOC */
60
61 #if !defined (HAVE_GETPW_DECLS)
62 extern struct passwd *getpwuid (), *getpwnam ();
63 #endif /* !HAVE_GETPW_DECLS */
64
65 #if !defined (savestring)
66 #  ifndef strcpy
67 extern char *strcpy ();
68 #  endif
69 #define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x))
70 #endif /* !savestring */
71
72 #if !defined (NULL)
73 #  if defined (__STDC__)
74 #    define NULL ((void *) 0)
75 #  else
76 #    define NULL 0x0
77 #  endif /* !__STDC__ */
78 #endif /* !NULL */
79
80 /* If being compiled as part of bash, these will be satisfied from
81    variables.o.  If being compiled as part of readline, they will
82    be satisfied from shell.o. */
83 extern char *get_home_dir __P((void));
84 extern char *get_env_value __P((char *));
85
86 /* The default value of tilde_additional_prefixes.  This is set to
87    whitespace preceding a tilde so that simple programs which do not
88    perform any word separation get desired behaviour. */
89 static char *default_prefixes[] =
90   { " ~", "\t~", (char *)NULL };
91
92 /* The default value of tilde_additional_suffixes.  This is set to
93    whitespace or newline so that simple programs which do not
94    perform any word separation get desired behaviour. */
95 static char *default_suffixes[] =
96   { " ", "\n", (char *)NULL };
97
98 /* If non-null, this contains the address of a function that the application
99    wants called before trying the standard tilde expansions.  The function
100    is called with the text sans tilde, and returns a malloc()'ed string
101    which is the expansion, or a NULL pointer if the expansion fails. */
102 CPFunction *tilde_expansion_preexpansion_hook = (CPFunction *)NULL;
103
104 /* If non-null, this contains the address of a function to call if the
105    standard meaning for expanding a tilde fails.  The function is called
106    with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
107    which is the expansion, or a NULL pointer if there is no expansion. */
108 CPFunction *tilde_expansion_failure_hook = (CPFunction *)NULL;
109
110 /* When non-null, this is a NULL terminated array of strings which
111    are duplicates for a tilde prefix.  Bash uses this to expand
112    `=~' and `:~'. */
113 char **tilde_additional_prefixes = default_prefixes;
114
115 /* When non-null, this is a NULL terminated array of strings which match
116    the end of a username, instead of just "/".  Bash sets this to
117    `:' and `=~'. */
118 char **tilde_additional_suffixes = default_suffixes;
119
120 /* Find the start of a tilde expansion in STRING, and return the index of
121    the tilde which starts the expansion.  Place the length of the text
122    which identified this tilde starter in LEN, excluding the tilde itself. */
123 static int
124 tilde_find_prefix (string, len)
125      char *string;
126      int *len;
127 {
128   register int i, j, string_len;
129   register char **prefixes;
130
131   prefixes = tilde_additional_prefixes;
132
133   string_len = strlen (string);
134   *len = 0;
135
136   if (*string == '\0' || *string == '~')
137     return (0);
138
139   if (prefixes)
140     {
141       for (i = 0; i < string_len; i++)
142         {
143           for (j = 0; prefixes[j]; j++)
144             {
145               if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
146                 {
147                   *len = strlen (prefixes[j]) - 1;
148                   return (i + *len);
149                 }
150             }
151         }
152     }
153   return (string_len);
154 }
155
156 /* Find the end of a tilde expansion in STRING, and return the index of
157    the character which ends the tilde definition.  */
158 static int
159 tilde_find_suffix (string)
160      char *string;
161 {
162   register int i, j, string_len;
163   register char **suffixes;
164
165   suffixes = tilde_additional_suffixes;
166   string_len = strlen (string);
167
168   for (i = 0; i < string_len; i++)
169     {
170 #if defined (__MSDOS__)
171       if (string[i] == '/' || string[i] == '\\' /* || !string[i] */)
172 #else
173       if (string[i] == '/' /* || !string[i] */)
174 #endif
175         break;
176
177       for (j = 0; suffixes && suffixes[j]; j++)
178         {
179           if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
180             return (i);
181         }
182     }
183   return (i);
184 }
185
186 /* Return a new string which is the result of tilde expanding STRING. */
187 char *
188 tilde_expand (string)
189      char *string;
190 {
191   char *result;
192   int result_size, result_index;
193
194   result_index = result_size = 0;
195   if (result = strchr (string, '~'))
196     result = xmalloc (result_size = (strlen (string) + 16));
197   else
198     result = xmalloc (result_size = (strlen (string) + 1));
199
200   /* Scan through STRING expanding tildes as we come to them. */
201   while (1)
202     {
203       register int start, end;
204       char *tilde_word, *expansion;
205       int len;
206
207       /* Make START point to the tilde which starts the expansion. */
208       start = tilde_find_prefix (string, &len);
209
210       /* Copy the skipped text into the result. */
211       if ((result_index + start + 1) > result_size)
212         result = xrealloc (result, 1 + (result_size += (start + 20)));
213
214       strncpy (result + result_index, string, start);
215       result_index += start;
216
217       /* Advance STRING to the starting tilde. */
218       string += start;
219
220       /* Make END be the index of one after the last character of the
221          username. */
222       end = tilde_find_suffix (string);
223
224       /* If both START and END are zero, we are all done. */
225       if (!start && !end)
226         break;
227
228       /* Expand the entire tilde word, and copy it into RESULT. */
229       tilde_word = xmalloc (1 + end);
230       strncpy (tilde_word, string, end);
231       tilde_word[end] = '\0';
232       string += end;
233
234       expansion = tilde_expand_word (tilde_word);
235       free (tilde_word);
236
237       len = strlen (expansion);
238 #ifdef __CYGWIN32__
239       /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
240          $HOME for `user' is /.  On cygwin, // denotes a network drive. */
241       if (len > 1 || *expansion != '/' || *string != '/')
242 #endif
243         {
244           if ((result_index + len + 1) > result_size)
245             result = xrealloc (result, 1 + (result_size += (len + 20)));
246
247           strcpy (result + result_index, expansion);
248           result_index += len;
249         }
250       free (expansion);
251     }
252
253   result[result_index] = '\0';
254
255   return (result);
256 }
257
258 /* Take FNAME and return the tilde prefix we want expanded.  If LENP is
259    non-null, the index of the end of the prefix into FNAME is returned in
260    the location it points to. */
261 static char *
262 isolate_tilde_prefix (fname, lenp)
263      char *fname;
264      int *lenp;
265 {
266   char *ret;
267   int i;
268
269   ret = xmalloc (strlen (fname));
270 #if defined (__MSDOS__)
271   for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++)
272 #else
273   for (i = 1; fname[i] && fname[i] != '/'; i++)
274 #endif
275     ret[i - 1] = fname[i];
276   ret[i - 1] = '\0';
277   if (lenp)
278     *lenp = i;
279   return ret;
280 }
281
282 /* Return a string that is PREFIX concatenated with SUFFIX starting at
283    SUFFIND. */
284 static char *
285 glue_prefix_and_suffix (prefix, suffix, suffind)
286      char *prefix, *suffix;
287      int suffind;
288 {
289   char *ret;
290   int plen, slen;
291
292   plen = (prefix && *prefix) ? strlen (prefix) : 0;
293   slen = strlen (suffix + suffind);
294   ret = xmalloc (plen + slen + 1);
295   if (plen)
296     strcpy (ret, prefix);
297   strcpy (ret + plen, suffix + suffind);
298   return ret;
299 }
300
301 /* Do the work of tilde expansion on FILENAME.  FILENAME starts with a
302    tilde.  If there is no expansion, call tilde_expansion_failure_hook.
303    This always returns a newly-allocated string, never static storage. */
304 char *
305 tilde_expand_word (filename)
306      char *filename;
307 {
308   char *dirname, *expansion, *username;
309   int user_len;
310   struct passwd *user_entry;
311
312   if (filename == 0)
313     return ((char *)NULL);
314
315   if (*filename != '~')
316     return (savestring (filename));
317
318   /* A leading `~/' or a bare `~' is *always* translated to the value of
319      $HOME or the home directory of the current user, regardless of any
320      preexpansion hook. */
321   if (filename[1] == '\0' || filename[1] == '/')
322     {
323       /* Prefix $HOME to the rest of the string. */
324       expansion = get_env_value ("HOME");
325
326       /* If there is no HOME variable, look up the directory in
327          the password database. */
328       if (expansion == 0)
329         expansion = get_home_dir ();
330
331       return (glue_prefix_and_suffix (expansion, filename, 1));
332     }
333
334   username = isolate_tilde_prefix (filename, &user_len);
335
336   if (tilde_expansion_preexpansion_hook)
337     {
338       expansion = (*tilde_expansion_preexpansion_hook) (username);
339       if (expansion)
340         {
341           dirname = glue_prefix_and_suffix (expansion, filename, user_len);
342           free (username);
343           free (expansion);
344           return (dirname);
345         }
346     }
347
348   /* No preexpansion hook, or the preexpansion hook failed.  Look in the
349      password database. */
350   dirname = (char *)NULL;
351   user_entry = getpwnam (username);
352   if (user_entry == 0)
353     {
354       /* If the calling program has a special syntax for expanding tildes,
355          and we couldn't find a standard expansion, then let them try. */
356       if (tilde_expansion_failure_hook)
357         {
358           expansion = (*tilde_expansion_failure_hook) (username);
359           if (expansion)
360             {
361               dirname = glue_prefix_and_suffix (expansion, filename, user_len);
362               free (expansion);
363             }
364         }
365       free (username);
366       /* If we don't have a failure hook, or if the failure hook did not
367          expand the tilde, return a copy of what we were passed. */
368       if (dirname == 0)
369         dirname = savestring (filename);
370     }
371   else
372     {
373       free (username);
374       dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
375     }
376
377   endpwent ();
378   return (dirname);
379 }
380
381 \f
382 #if defined (TEST)
383 #undef NULL
384 #include <stdio.h>
385
386 main (argc, argv)
387      int argc;
388      char **argv;
389 {
390   char *result, line[512];
391   int done = 0;
392
393   while (!done)
394     {
395       printf ("~expand: ");
396       fflush (stdout);
397
398       if (!gets (line))
399         strcpy (line, "done");
400
401       if ((strcmp (line, "done") == 0) ||
402           (strcmp (line, "quit") == 0) ||
403           (strcmp (line, "exit") == 0))
404         {
405           done = 1;
406           break;
407         }
408
409       result = tilde_expand (line);
410       printf ("  --> %s\n", result);
411       free (result);
412     }
413   exit (0);
414 }
415
416 static void memory_error_and_abort ();
417
418 static char *
419 xmalloc (bytes)
420      int bytes;
421 {
422   char *temp = (char *)malloc (bytes);
423
424   if (!temp)
425     memory_error_and_abort ();
426   return (temp);
427 }
428
429 static char *
430 xrealloc (pointer, bytes)
431      char *pointer;
432      int bytes;
433 {
434   char *temp;
435
436   if (!pointer)
437     temp = (char *)malloc (bytes);
438   else
439     temp = (char *)realloc (pointer, bytes);
440
441   if (!temp)
442     memory_error_and_abort ();
443
444   return (temp);
445 }
446
447 static void
448 memory_error_and_abort ()
449 {
450   fprintf (stderr, "readline: out of virtual memory\n");
451   abort ();
452 }
453
454 /*
455  * Local variables:
456  * compile-command: "gcc -g -DTEST -o tilde tilde.c"
457  * end:
458  */
459 #endif /* TEST */