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