1 /* expand_path.c -- expand environmental variables in passed in string
3 * Copyright (C) 1995-2005 The Free Software Foundation, Inc.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * The main routine is expand_path(), it is the routine that handles
16 * the '~' character in four forms:
21 * and handles environment variables contained within the pathname
22 * which are defined by:
23 * ${var_name} (var_name is the name of the environ variable)
24 * $var_name (var_name ends w/ non-alphanumeric char other than '_')
28 #include <sys/types.h>
34 static void variable_delproc (Node
*);
37 variable_delproc (Node
*node
)
42 /* Currently used by -s option; we might want a way to set user
43 variables in a file in the $CVSROOT/CVSROOT directory too. */
46 variable_set (char *nameval
)
53 while (isalnum ((unsigned char) *p
) || *p
== '_')
56 error (1, 0, "invalid character in user variable name in %s", nameval
);
58 error (1, 0, "empty user variable name in %s", nameval
);
59 name
= xmalloc (p
- nameval
+ 1);
60 strncpy (name
, nameval
, p
- nameval
);
61 name
[p
- nameval
] = '\0';
62 /* Make p point to the value. */
64 if (strchr (p
, '\012'))
65 error (1, 0, "linefeed in user variable value in %s", nameval
);
68 variable_list
= getlist ();
70 node
= findnode (variable_list
, name
);
74 node
->type
= VARIABLE
;
75 node
->delproc
= variable_delproc
;
77 node
->data
= xstrdup (p
);
78 (void) addnode (variable_list
, node
);
82 /* Replace the old value. For example, this means that -s
83 options on the command line override ones from .cvsrc. */
85 node
->data
= xstrdup (p
);
92 /* Expand variable NAME into its contents, per the rules above.
94 * CVSROOT is used to expanding $CVSROOT.
97 * A pointer to the requested variable contents or NULL when the requested
98 * variable is not found.
101 * None, though this function may generate warning messages when NAME is not
105 expand_variable (const char *name
, const char *cvsroot
,
106 const char *file
, int line
)
108 if (!strcmp (name
, CVSROOT_ENV
))
110 else if (!strcmp (name
, "RCSBIN"))
112 error (0, 0, "RCSBIN internal variable is no longer supported");
115 else if (!strcmp (name
, EDITOR1_ENV
))
117 else if (!strcmp (name
, EDITOR2_ENV
))
119 else if (!strcmp (name
, EDITOR3_ENV
))
121 else if (!strcmp (name
, "USER"))
123 else if (!strcmp (name
, "SESSIONID")
124 || !strcmp (name
, "COMMITID"))
125 return global_session_id
;
126 else if (isalpha (name
[0]))
128 /* These names are reserved for future versions of CVS,
129 so that is why it is an error. */
131 error (0, 0, "%s:%d: no such internal variable $%s",
134 error (0, 0, "%s: no such internal variable $%s",
138 else if (name
[0] == '=')
141 /* Crazy syntax for a user variable. But we want
142 *something* that lets the user name a user variable
143 anything he wants, without interference from
144 (existing or future) internal variables. */
145 node
= findnode (variable_list
, name
+ 1);
149 error (0, 0, "%s:%d: no such user variable ${%s}",
152 error (0, 0, "%s: no such user variable ${%s}",
160 /* It is an unrecognized character. We return an error to
161 reserve these for future versions of CVS; it is plausible
162 that various crazy syntaxes might be invented for inserting
163 information about revisions, branches, etc. */
165 error (0, 0, "%s:%d: unrecognized variable syntax %s",
168 error (0, 0, "%s: unrecognized variable syntax %s",
176 /* This routine will expand the pathname to account for ~ and $
177 * characters as described above. Returns a pointer to a newly
178 * malloc'd string. If an error occurs, an error message is printed
179 * via error() and NULL is returned. FILE and LINE are the filename
180 * and linenumber to include in the error message. FILE must point
181 * to something; LINE can be zero to indicate the line number is not
184 * When FORMATSAFE is set, percent signs (`%') in variable contents are doubled
185 * to prevent later expansion by format_cmdline.
187 * CVSROOT is used to expanding $CVSROOT.
190 expand_path (const char *name
, const char *cvsroot
, bool formatsafe
,
191 const char *file
, int line
)
197 size_t mybuf_size
= 0;
201 char inquotes
= '\0';
205 /* Sorry this routine is so ugly; it is a head-on collision
206 between the `traditional' unix *d++ style and the need to
207 dynamically allocate. It would be much cleaner (and probably
208 faster, not that this is a bottleneck for CVS) with more use of
209 strcpy & friends, but I haven't taken the effort to rewrite it
212 /* First copy from NAME to MYBUF, expanding $<foo> as we go. */
214 expand_string (&mybuf
, &mybuf_size
, d
+ 1);
215 while ((mybuf
[d
++] = name
[s
]) != '\0')
219 /* The next character is a literal. Leave the \ in the string
220 * since it will be needed again when the string is split into
223 /* if we have a \ as the last character of the string, just leave
224 * it there - this is where we would set the escape flag to tell
225 * our parent we want another line if we cared.
229 expand_string (&mybuf
, &mybuf_size
, d
+ 1);
230 mybuf
[d
++] = name
[s
++];
233 /* skip $ variable processing for text inside single quotes */
234 else if (inquotes
== '\'')
236 if (name
[s
++] == '\'')
241 else if (name
[s
] == '\'')
246 else if (name
[s
] == '"')
249 if (inquotes
) inquotes
= '\0';
252 else if (name
[s
++] == '$')
254 int flag
= (name
[s
] == '{');
257 expand_string (&mybuf
, &mybuf_size
, d
+ 1);
258 for (; (mybuf
[d
++] = name
[s
]); s
++)
262 : !isalnum (name
[s
]) && name
[s
] != '_')
264 expand_string (&mybuf
, &mybuf_size
, d
+ 1);
267 e
= expand_variable (&mybuf
[p
+flag
], cvsroot
, file
, line
);
271 expand_string (&mybuf
, &mybuf_size
, d
+ 1);
272 for (d
= p
- 1; (mybuf
[d
++] = *e
++); )
274 expand_string (&mybuf
, &mybuf_size
, d
+ 1);
275 if (mybuf
[d
-1] == '"')
277 /* escape the double quotes if we're between a matched
278 * pair of double quotes so that this sub will be
279 * passed inside as or as part of a single argument
280 * during the argument split later.
285 expand_string (&mybuf
, &mybuf_size
, d
+ 1);
289 else if (formatsafe
&& mybuf
[d
-1] == '%')
291 /* escape '%' to get past printf style format strings
292 * later (in make_cmdline).
294 expand_string (&mybuf
, &mybuf_size
, d
+ 1);
304 /* expand_variable has already printed an error message. */
307 expand_string (&mybuf
, &mybuf_size
, d
+ 1);
309 expand_string (&mybuf
, &mybuf_size
, d
+ 1);
312 /* Then copy from MYBUF to BUF, expanding ~. */
314 /* If you don't want ~username ~/ to be expanded simply remove
315 * This entire if statement including the else portion
320 while (mybuf
[++s
] != '/' && mybuf
[s
] != '\0')
322 expand_string (&buf
, &buf_size
, p
+ 1);
325 expand_string (&buf
, &buf_size
, p
+ 1);
332 #ifdef GETPWNAM_MISSING
335 "%s:%d:tilde expansion not supported on this system",
338 error (0, 0, "%s:tilde expansion not supported on this system",
343 ps
= getpwnam (buf
+ d
);
347 error (0, 0, "%s:%d: no such user %s",
348 file
, line
, buf
+ d
);
350 error (0, 0, "%s: no such user %s", file
, buf
+ d
);
357 error (1, 0, "cannot find home directory");
360 expand_string (&buf
, &buf_size
, d
+ p
);
361 memcpy (buf
+ d
, e
, p
);
364 /* Kill up to here */
365 p
= strlen (mybuf
+ s
) + 1;
366 expand_string (&buf
, &buf_size
, d
+ p
);
367 memcpy (buf
+ d
, mybuf
+ s
, p
);
369 /* OK, buf contains the value we want to return. Clean up and return
372 /* Save a little memory with xstrdup; buf will tend to allocate
373 more than it needs to. */
374 result
= xstrdup (buf
);
379 if (mybuf
) free (mybuf
);