1 /* expand_path.c -- expand environmental variables in passed in string
3 * The main routine is expand_path(), it is the routine that handles
4 * the '~' character in four forms:
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 '_')
16 #include <sys/types.h>
18 static char *expand_variable (const char *env, const char *file, int line);
22 List *variable_list = NULL;
24 static void variable_delproc (Node *);
27 variable_delproc (Node *node)
32 /* Currently used by -s option; we might want a way to set user
33 variables in a file in the $CVSROOT/CVSROOT directory too. */
36 variable_set (char *nameval)
43 while (isalnum ((unsigned char) *p) || *p == '_')
46 error ( 1, 0, "invalid character in user variable name in %s",
49 error (1, 0, "empty user variable name in %s", nameval);
50 name = xmalloc (p - nameval + 1);
51 strncpy (name, nameval, p - nameval);
52 name[p - nameval] = '\0';
53 /* Make p point to the value. */
55 if (strchr (p, '\012') != NULL)
56 error (1, 0, "linefeed in user variable value in %s", nameval);
58 if (variable_list == NULL)
59 variable_list = getlist ();
61 node = findnode (variable_list, name);
65 node->type = VARIABLE;
66 node->delproc = variable_delproc;
68 node->data = xstrdup (p);
69 (void) addnode (variable_list, node);
73 /* Replace the old value. For example, this means that -s
74 options on the command line override ones from .cvsrc. */
76 node->data = xstrdup (p);
83 /* This routine will expand the pathname to account for ~ and $
84 characters as described above. Returns a pointer to a newly
85 malloc'd string. If an error occurs, an error message is printed
86 via error() and NULL is returned. FILE and LINE are the filename
87 and linenumber to include in the error message. FILE must point
88 to something; LINE can be zero to indicate the line number is not
91 expand_path (const char *name, const char *file, int line, int formatsafe)
97 size_t mybuf_size = 0;
102 char inquotes = '\0';
106 /* Sorry this routine is so ugly; it is a head-on collision
107 between the `traditional' unix *d++ style and the need to
108 dynamically allocate. It would be much cleaner (and probably
109 faster, not that this is a bottleneck for CVS) with more use of
110 strcpy & friends, but I haven't taken the effort to rewrite it
113 /* First copy from NAME to MYBUF, expanding $<foo> as we go. */
117 expand_string (&mybuf, &mybuf_size, doff + 1);
119 while ((*d++ = *s) != '\0')
123 /* The next character is a literal. Leave the \ in the string since
124 * it will be needed again when the string is split into arguments.
126 /* if we have a \ as the last character of the string, just leave it there
127 * - this is where we would set the escape flag to tell our parent we want
128 * another line if we cared.
133 expand_string (&mybuf, &mybuf_size, doff + 1);
138 /* skip $ variable processing for text inside single quotes */
139 else if (inquotes == '\'')
154 if (inquotes) inquotes = '\0';
157 else if (*s++ == '$')
161 int flag = (*s == '{');
164 expand_string (&mybuf, &mybuf_size, doff + 1);
166 for (; (*d++ = *s); s++)
170 : isalnum ((unsigned char) *s) == 0 && *s != '_')
173 expand_string (&mybuf, &mybuf_size, doff + 1);
177 e = expand_variable (&p[flag], file, line);
182 expand_string (&mybuf, &mybuf_size, doff + 1);
184 for (d = &p[-1]; (*d++ = *e++);)
187 expand_string (&mybuf, &mybuf_size, doff + 1);
191 /* escape the double quotes if we're between a matched pair of
192 * double quotes so that this sub will be passed inside as or as
193 * part of a single argument during the argument split later.
199 expand_string (&mybuf, &mybuf_size, doff + 1);
204 else if (formatsafe && d[-1] == '%')
206 /* escape '%' to get past printf style format strings later
210 expand_string (&mybuf, &mybuf_size, doff + 1);
220 /* expand_variable has already printed an error message. */
224 expand_string (&mybuf, &mybuf_size, doff + 1);
228 expand_string (&mybuf, &mybuf_size, doff + 1);
232 /* Then copy from MYBUF to BUF, expanding ~. */
235 /* If you don't want ~username ~/ to be expanded simply remove
236 * This entire if statement including the else portion
242 pstart = p = xstrdup (s);
243 if (*pstart=='/' || *pstart==0)
247 #ifdef GETPWNAM_MISSING
248 for (; *p!='/' && *p; p++)
253 "%s:%d:tilde expansion not supported on this system",
256 error (0, 0, "%s:tilde expansion not supported on this system",
261 for (; *p!='/' && *p; p++)
264 ps = getpwnam (pstart);
268 error (0, 0, "%s:%d: no such user %s",
271 error (0, 0, "%s: no such user %s", file, pstart);
278 error (1, 0, "cannot find home directory");
281 expand_string (&buf, &buf_size, doff + 1);
283 while ((*d++ = *t++))
286 expand_string (&buf, &buf_size, doff + 1);
295 /* Kill up to here */
297 expand_string (&buf, &buf_size, doff + 1);
299 while ((*d++ = *s++))
302 expand_string (&buf, &buf_size, doff + 1);
306 expand_string (&buf, &buf_size, doff + 1);
310 /* OK, buf contains the value we want to return. Clean up and return
313 /* Save a little memory with xstrdup; buf will tend to allocate
314 more than it needs to. */
315 result = xstrdup (buf);
330 expand_variable (const char *name, const char *file, int line)
332 if (strcmp (name, CVSROOT_ENV) == 0)
333 return current_parsed_root->directory;
334 else if (strcmp (name, "RCSBIN") == 0)
336 error (0, 0, "RCSBIN internal variable is no longer supported");
339 else if (strcmp (name, EDITOR1_ENV) == 0)
341 else if (strcmp (name, EDITOR2_ENV) == 0)
343 else if (strcmp (name, EDITOR3_ENV) == 0)
345 else if (strcmp (name, "USER") == 0)
347 else if (isalpha ((unsigned char) name[0]))
349 /* These names are reserved for future versions of CVS,
350 so that is why it is an error. */
352 error (0, 0, "%s:%d: no such internal variable $%s",
355 error (0, 0, "%s: no such internal variable $%s",
359 else if (name[0] == '=')
362 /* Crazy syntax for a user variable. But we want
363 *something* that lets the user name a user variable
364 anything he wants, without interference from
365 (existing or future) internal variables. */
366 node = findnode (variable_list, name + 1);
370 error (0, 0, "%s:%d: no such user variable ${%s}",
373 error (0, 0, "%s: no such user variable ${%s}",
381 /* It is an unrecognized character. We return an error to
382 reserve these for future versions of CVS; it is plausible
383 that various crazy syntaxes might be invented for inserting
384 information about revisions, branches, etc. */
386 error (0, 0, "%s:%d: unrecognized variable syntax %s",
389 error (0, 0, "%s: unrecognized variable syntax %s",