source: trunk/essentials/sys-apps/texinfo/info/tilde.c

Last change on this file was 2617, checked in by bird, 19 years ago

GNU Texinfo 4.8

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