]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/cvs/src/expand_path.c
This commit was generated by cvs2svn to compensate for changes in r172314,
[FreeBSD/FreeBSD.git] / contrib / cvs / src / expand_path.c
1 /* expand_path.c -- expand environmental variables in passed in string
2  *
3  * The main routine is expand_path(), it is the routine that handles
4  * the '~' character in four forms: 
5  *     ~name
6  *     ~name/
7  *     ~/
8  *     ~
9  * and handles environment variables contained within the pathname
10  * which are defined by:
11  *     ${var_name}   (var_name is the name of the environ variable)
12  *     $var_name     (var_name ends w/ non-alphanumeric char other than '_')
13  */
14
15 #include "cvs.h"
16 #include <sys/types.h>
17
18 static char *expand_variable PROTO((const char *env, const char *file,
19                                     int line));
20
21
22
23 /* User variables.  */
24
25 List *variable_list = NULL;
26
27 static void variable_delproc PROTO ((Node *));
28
29 static void
30 variable_delproc (node)
31     Node *node;
32 {
33     free (node->data);
34 }
35
36 /* Currently used by -s option; we might want a way to set user
37    variables in a file in the $CVSROOT/CVSROOT directory too.  */
38
39 void
40 variable_set (nameval)
41     char *nameval;
42 {
43     char *p;
44     char *name;
45     Node *node;
46
47     p = nameval;
48     while (isalnum ((unsigned char) *p) || *p == '_')
49         ++p;
50     if (*p != '=')
51         error (1, 0, "illegal character in user variable name in %s", nameval);
52     if (p == nameval)
53         error (1, 0, "empty user variable name in %s", nameval);
54     name = xmalloc (p - nameval + 1);
55     strncpy (name, nameval, p - nameval);
56     name[p - nameval] = '\0';
57     /* Make p point to the value.  */
58     ++p;
59     if (strchr (p, '\012') != NULL)
60         error (1, 0, "linefeed in user variable value in %s", nameval);
61
62     if (variable_list == NULL)
63         variable_list = getlist ();
64
65     node = findnode (variable_list, name);
66     if (node == NULL)
67     {
68         node = getnode ();
69         node->type = VARIABLE;
70         node->delproc = variable_delproc;
71         node->key = name;
72         node->data = xstrdup (p);
73         (void) addnode (variable_list, node);
74     }
75     else
76     {
77         /* Replace the old value.  For example, this means that -s
78            options on the command line override ones from .cvsrc.  */
79         free (node->data);
80         node->data = xstrdup (p);
81         free (name);
82     }
83 }
84
85
86
87 /* This routine will expand the pathname to account for ~ and $
88    characters as described above.  Returns a pointer to a newly
89    malloc'd string.  If an error occurs, an error message is printed
90    via error() and NULL is returned.  FILE and LINE are the filename
91    and linenumber to include in the error message.  FILE must point
92    to something; LINE can be zero to indicate the line number is not
93    known.  */
94 char *
95 expand_path (name, file, line)
96     const char *name;
97     const char *file;
98     int line;
99 {
100     const char *s;
101     char *d;
102
103     char *mybuf = NULL;
104     size_t mybuf_size = 0;
105     char *buf = NULL;
106     size_t buf_size = 0;
107
108     size_t doff;
109
110     char *result;
111
112     /* Sorry this routine is so ugly; it is a head-on collision
113        between the `traditional' unix *d++ style and the need to
114        dynamically allocate.  It would be much cleaner (and probably
115        faster, not that this is a bottleneck for CVS) with more use of
116        strcpy & friends, but I haven't taken the effort to rewrite it
117        thusly.  */
118
119     /* First copy from NAME to MYBUF, expanding $<foo> as we go.  */
120     s = name;
121     d = mybuf;
122     doff = d - mybuf;
123     expand_string (&mybuf, &mybuf_size, doff + 1);
124     d = mybuf + doff;
125     while ((*d++ = *s))
126     {
127         if (*s++ == '$')
128         {
129             char *p = d;
130             char *e;
131             int flag = (*s == '{');
132
133             doff = d - mybuf;
134             expand_string (&mybuf, &mybuf_size, doff + 1);
135             d = mybuf + doff;
136             for (; (*d++ = *s); s++)
137             {
138                 if (flag
139                     ? *s =='}'
140                     : isalnum ((unsigned char) *s) == 0 && *s != '_')
141                     break;
142                 doff = d - mybuf;
143                 expand_string (&mybuf, &mybuf_size, doff + 1);
144                 d = mybuf + doff;
145             }
146             *--d = '\0';
147             e = expand_variable (&p[flag], file, line);
148
149             if (e)
150             {
151                 doff = d - mybuf;
152                 expand_string (&mybuf, &mybuf_size, doff + 1);
153                 d = mybuf + doff;
154                 for (d = &p[-1]; (*d++ = *e++);)
155                 {
156                     doff = d - mybuf;
157                     expand_string (&mybuf, &mybuf_size, doff + 1);
158                     d = mybuf + doff;
159                 }
160                 --d;
161                 if (flag && *s)
162                     s++;
163             }
164             else
165                 /* expand_variable has already printed an error message.  */
166                 goto error_exit;
167         }
168         doff = d - mybuf;
169         expand_string (&mybuf, &mybuf_size, doff + 1);
170         d = mybuf + doff;
171     }
172     doff = d - mybuf;
173     expand_string (&mybuf, &mybuf_size, doff + 1);
174     d = mybuf + doff;
175     *d = '\0';
176
177     /* Then copy from MYBUF to BUF, expanding ~.  */
178     s = mybuf;
179     d = buf;
180     /* If you don't want ~username ~/ to be expanded simply remove
181      * This entire if statement including the else portion
182      */
183     if (*s++ == '~')
184     {
185         char *t;
186         char *p, *pstart;
187         pstart = p = xstrdup (s);
188         if (*pstart=='/' || *pstart==0)
189             t = get_homedir ();
190         else
191         {
192 #ifdef GETPWNAM_MISSING
193             for (; *p!='/' && *p; p++)
194                 ;
195             *p = 0;
196             if (line != 0)
197                 error (0, 0,
198                        "%s:%d:tilde expansion not supported on this system",
199                        file, line);
200             else
201                 error (0, 0, "%s:tilde expansion not supported on this system",
202                        file);
203             return NULL;
204 #else
205             struct passwd *ps;
206             for (; *p!='/' && *p; p++)
207                 ;
208             *p = 0;
209             ps = getpwnam (pstart);
210             if (ps == 0)
211             {
212                 if (line != 0)
213                     error (0, 0, "%s:%d: no such user %s",
214                            file, line, pstart);
215                 else
216                     error (0, 0, "%s: no such user %s", file, pstart);
217                 return NULL;
218             }
219             t = ps->pw_dir;
220 #endif
221         }
222         if (t == NULL)
223             error (1, 0, "cannot find home directory");
224
225         doff = d - buf;
226         expand_string (&buf, &buf_size, doff + 1);
227         d = buf + doff;
228         while ((*d++ = *t++))
229         {
230             doff = d - buf;
231             expand_string (&buf, &buf_size, doff + 1);
232             d = buf + doff;
233         }
234         --d;
235         s+=p-pstart;
236         free (pstart);
237     }
238     else
239         --s;
240         /* Kill up to here */
241     doff = d - buf;
242     expand_string (&buf, &buf_size, doff + 1);
243     d = buf + doff;
244     while ((*d++ = *s++))
245     {
246         doff = d - buf;
247         expand_string (&buf, &buf_size, doff + 1);
248         d = buf + doff;
249     }
250     doff = d - buf;
251     expand_string (&buf, &buf_size, doff + 1);
252     d = buf + doff;
253     *d = '\0';
254
255     /* OK, buf contains the value we want to return.  Clean up and return
256        it.  */
257     free (mybuf);
258     /* Save a little memory with xstrdup; buf will tend to allocate
259        more than it needs to.  */
260     result = xstrdup (buf);
261     free (buf);
262     return result;
263
264  error_exit:
265     if (mybuf != NULL)
266         free (mybuf);
267     if (buf != NULL)
268         free (buf);
269     return NULL;
270 }
271
272 static char *
273 expand_variable (name, file, line)
274     const char *name;
275     const char *file;
276     int line;
277 {
278     if (strcmp (name, CVSROOT_ENV) == 0)
279         return current_parsed_root->directory;
280     else if (strcmp (name, "RCSBIN") == 0)
281     {
282         error (0, 0, "RCSBIN internal variable is no longer supported");
283         return NULL;
284     }
285     else if (strcmp (name, EDITOR1_ENV) == 0)
286         return Editor;
287     else if (strcmp (name, EDITOR2_ENV) == 0)
288         return Editor;
289     else if (strcmp (name, EDITOR3_ENV) == 0)
290         return Editor;
291     else if (strcmp (name, "USER") == 0)
292         return getcaller ();
293     else if (isalpha ((unsigned char) name[0]))
294     {
295         /* These names are reserved for future versions of CVS,
296            so that is why it is an error.  */
297         if (line != 0)
298             error (0, 0, "%s:%d: no such internal variable $%s",
299                    file, line, name);
300         else
301             error (0, 0, "%s: no such internal variable $%s",
302                    file, name);
303         return NULL;
304     }
305     else if (name[0] == '=')
306     {
307         Node *node;
308         /* Crazy syntax for a user variable.  But we want
309            *something* that lets the user name a user variable
310            anything he wants, without interference from
311            (existing or future) internal variables.  */
312         node = findnode (variable_list, name + 1);
313         if (node == NULL)
314         {
315             if (line != 0)
316                 error (0, 0, "%s:%d: no such user variable ${%s}",
317                        file, line, name);
318             else
319                 error (0, 0, "%s: no such user variable ${%s}",
320                        file, name);
321             return NULL;
322         }
323         return node->data;
324     }
325     else
326     {
327         /* It is an unrecognized character.  We return an error to
328            reserve these for future versions of CVS; it is plausible
329            that various crazy syntaxes might be invented for inserting
330            information about revisions, branches, etc.  */
331         if (line != 0)
332             error (0, 0, "%s:%d: unrecognized variable syntax %s",
333                    file, line, name);
334         else
335             error (0, 0, "%s: unrecognized variable syntax %s",
336                    file, name);
337         return NULL;
338     }
339 }