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