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