OSDN Git Service

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