1*86d7f5d3SJohn Marino /* expand_path.c -- expand environmental variables in passed in string
2*86d7f5d3SJohn Marino  *
3*86d7f5d3SJohn Marino  * Copyright (C) 1995-2005 The Free Software Foundation, Inc.
4*86d7f5d3SJohn Marino  *
5*86d7f5d3SJohn Marino  * This program is free software; you can redistribute it and/or modify
6*86d7f5d3SJohn Marino  * it under the terms of the GNU General Public License as published by
7*86d7f5d3SJohn Marino  * the Free Software Foundation; either version 2, or (at your option)
8*86d7f5d3SJohn Marino  * any later version.
9*86d7f5d3SJohn Marino  *
10*86d7f5d3SJohn Marino  * This program is distributed in the hope that it will be useful,
11*86d7f5d3SJohn Marino  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12*86d7f5d3SJohn Marino  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13*86d7f5d3SJohn Marino  * GNU General Public License for more details.
14*86d7f5d3SJohn Marino  *
15*86d7f5d3SJohn Marino  * The main routine is expand_path(), it is the routine that handles
16*86d7f5d3SJohn Marino  * the '~' character in four forms:
17*86d7f5d3SJohn Marino  *     ~name
18*86d7f5d3SJohn Marino  *     ~name/
19*86d7f5d3SJohn Marino  *     ~/
20*86d7f5d3SJohn Marino  *     ~
21*86d7f5d3SJohn Marino  * and handles environment variables contained within the pathname
22*86d7f5d3SJohn Marino  * which are defined by:
23*86d7f5d3SJohn Marino  *     ${var_name}   (var_name is the name of the environ variable)
24*86d7f5d3SJohn Marino  *     $var_name     (var_name ends w/ non-alphanumeric char other than '_')
25*86d7f5d3SJohn Marino  */
26*86d7f5d3SJohn Marino 
27*86d7f5d3SJohn Marino #include "cvs.h"
28*86d7f5d3SJohn Marino #include <sys/types.h>
29*86d7f5d3SJohn Marino 
30*86d7f5d3SJohn Marino /* User variables.  */
31*86d7f5d3SJohn Marino 
32*86d7f5d3SJohn Marino List *variable_list;
33*86d7f5d3SJohn Marino 
34*86d7f5d3SJohn Marino static void variable_delproc (Node *);
35*86d7f5d3SJohn Marino 
36*86d7f5d3SJohn Marino static void
variable_delproc(Node * node)37*86d7f5d3SJohn Marino variable_delproc (Node *node)
38*86d7f5d3SJohn Marino {
39*86d7f5d3SJohn Marino     free (node->data);
40*86d7f5d3SJohn Marino }
41*86d7f5d3SJohn Marino 
42*86d7f5d3SJohn Marino /* Currently used by -s option; we might want a way to set user
43*86d7f5d3SJohn Marino    variables in a file in the $CVSROOT/CVSROOT directory too.  */
44*86d7f5d3SJohn Marino 
45*86d7f5d3SJohn Marino void
variable_set(char * nameval)46*86d7f5d3SJohn Marino variable_set (char *nameval)
47*86d7f5d3SJohn Marino {
48*86d7f5d3SJohn Marino     char *p;
49*86d7f5d3SJohn Marino     char *name;
50*86d7f5d3SJohn Marino     Node *node;
51*86d7f5d3SJohn Marino 
52*86d7f5d3SJohn Marino     p = nameval;
53*86d7f5d3SJohn Marino     while (isalnum ((unsigned char) *p) || *p == '_')
54*86d7f5d3SJohn Marino 	++p;
55*86d7f5d3SJohn Marino     if (*p != '=')
56*86d7f5d3SJohn Marino 	error (1, 0, "invalid character in user variable name in %s", nameval);
57*86d7f5d3SJohn Marino     if (p == nameval)
58*86d7f5d3SJohn Marino 	error (1, 0, "empty user variable name in %s", nameval);
59*86d7f5d3SJohn Marino     name = xmalloc (p - nameval + 1);
60*86d7f5d3SJohn Marino     strncpy (name, nameval, p - nameval);
61*86d7f5d3SJohn Marino     name[p - nameval] = '\0';
62*86d7f5d3SJohn Marino     /* Make p point to the value.  */
63*86d7f5d3SJohn Marino     ++p;
64*86d7f5d3SJohn Marino     if (strchr (p, '\012'))
65*86d7f5d3SJohn Marino 	error (1, 0, "linefeed in user variable value in %s", nameval);
66*86d7f5d3SJohn Marino 
67*86d7f5d3SJohn Marino     if (!variable_list)
68*86d7f5d3SJohn Marino 	variable_list = getlist ();
69*86d7f5d3SJohn Marino 
70*86d7f5d3SJohn Marino     node = findnode (variable_list, name);
71*86d7f5d3SJohn Marino     if (!node)
72*86d7f5d3SJohn Marino     {
73*86d7f5d3SJohn Marino 	node = getnode ();
74*86d7f5d3SJohn Marino 	node->type = VARIABLE;
75*86d7f5d3SJohn Marino 	node->delproc = variable_delproc;
76*86d7f5d3SJohn Marino 	node->key = name;
77*86d7f5d3SJohn Marino 	node->data = xstrdup (p);
78*86d7f5d3SJohn Marino 	(void) addnode (variable_list, node);
79*86d7f5d3SJohn Marino     }
80*86d7f5d3SJohn Marino     else
81*86d7f5d3SJohn Marino     {
82*86d7f5d3SJohn Marino 	/* Replace the old value.  For example, this means that -s
83*86d7f5d3SJohn Marino 	   options on the command line override ones from .cvsrc.  */
84*86d7f5d3SJohn Marino 	free (node->data);
85*86d7f5d3SJohn Marino 	node->data = xstrdup (p);
86*86d7f5d3SJohn Marino 	free (name);
87*86d7f5d3SJohn Marino     }
88*86d7f5d3SJohn Marino }
89*86d7f5d3SJohn Marino 
90*86d7f5d3SJohn Marino 
91*86d7f5d3SJohn Marino 
92*86d7f5d3SJohn Marino /* Expand variable NAME into its contents, per the rules above.
93*86d7f5d3SJohn Marino  *
94*86d7f5d3SJohn Marino  * CVSROOT is used to expanding $CVSROOT.
95*86d7f5d3SJohn Marino  *
96*86d7f5d3SJohn Marino  * RETURNS
97*86d7f5d3SJohn Marino  *   A pointer to the requested variable contents or NULL when the requested
98*86d7f5d3SJohn Marino  *   variable is not found.
99*86d7f5d3SJohn Marino  *
100*86d7f5d3SJohn Marino  * ERRORS
101*86d7f5d3SJohn Marino  *   None, though this function may generate warning messages when NAME is not
102*86d7f5d3SJohn Marino  *   found.
103*86d7f5d3SJohn Marino  */
104*86d7f5d3SJohn Marino static const char *
expand_variable(const char * name,const char * cvsroot,const char * file,int line)105*86d7f5d3SJohn Marino expand_variable (const char *name, const char *cvsroot,
106*86d7f5d3SJohn Marino 		 const char *file, int line)
107*86d7f5d3SJohn Marino {
108*86d7f5d3SJohn Marino     if (!strcmp (name, CVSROOT_ENV))
109*86d7f5d3SJohn Marino 	return cvsroot;
110*86d7f5d3SJohn Marino     else if (!strcmp (name, "RCSBIN"))
111*86d7f5d3SJohn Marino     {
112*86d7f5d3SJohn Marino 	error (0, 0, "RCSBIN internal variable is no longer supported");
113*86d7f5d3SJohn Marino 	return NULL;
114*86d7f5d3SJohn Marino     }
115*86d7f5d3SJohn Marino     else if (!strcmp (name, EDITOR1_ENV))
116*86d7f5d3SJohn Marino 	return Editor;
117*86d7f5d3SJohn Marino     else if (!strcmp (name, EDITOR2_ENV))
118*86d7f5d3SJohn Marino 	return Editor;
119*86d7f5d3SJohn Marino     else if (!strcmp (name, EDITOR3_ENV))
120*86d7f5d3SJohn Marino 	return Editor;
121*86d7f5d3SJohn Marino     else if (!strcmp (name, "USER"))
122*86d7f5d3SJohn Marino 	return getcaller ();
123*86d7f5d3SJohn Marino     else if (!strcmp (name, "SESSIONID")
124*86d7f5d3SJohn Marino 	     || !strcmp (name, "COMMITID"))
125*86d7f5d3SJohn Marino 	return global_session_id;
126*86d7f5d3SJohn Marino     else if (isalpha (name[0]))
127*86d7f5d3SJohn Marino     {
128*86d7f5d3SJohn Marino 	/* These names are reserved for future versions of CVS,
129*86d7f5d3SJohn Marino 	   so that is why it is an error.  */
130*86d7f5d3SJohn Marino 	if (line)
131*86d7f5d3SJohn Marino 	    error (0, 0, "%s:%d: no such internal variable $%s",
132*86d7f5d3SJohn Marino 		   file, line, name);
133*86d7f5d3SJohn Marino 	else
134*86d7f5d3SJohn Marino 	    error (0, 0, "%s: no such internal variable $%s",
135*86d7f5d3SJohn Marino 		   file, name);
136*86d7f5d3SJohn Marino 	return NULL;
137*86d7f5d3SJohn Marino     }
138*86d7f5d3SJohn Marino     else if (name[0] == '=')
139*86d7f5d3SJohn Marino     {
140*86d7f5d3SJohn Marino 	Node *node;
141*86d7f5d3SJohn Marino 	/* Crazy syntax for a user variable.  But we want
142*86d7f5d3SJohn Marino 	   *something* that lets the user name a user variable
143*86d7f5d3SJohn Marino 	   anything he wants, without interference from
144*86d7f5d3SJohn Marino 	   (existing or future) internal variables.  */
145*86d7f5d3SJohn Marino 	node = findnode (variable_list, name + 1);
146*86d7f5d3SJohn Marino 	if (!node)
147*86d7f5d3SJohn Marino 	{
148*86d7f5d3SJohn Marino 	    if (line)
149*86d7f5d3SJohn Marino 		error (0, 0, "%s:%d: no such user variable ${%s}",
150*86d7f5d3SJohn Marino 		       file, line, name);
151*86d7f5d3SJohn Marino 	    else
152*86d7f5d3SJohn Marino 		error (0, 0, "%s: no such user variable ${%s}",
153*86d7f5d3SJohn Marino 		       file, name);
154*86d7f5d3SJohn Marino 	    return NULL;
155*86d7f5d3SJohn Marino 	}
156*86d7f5d3SJohn Marino 	return node->data;
157*86d7f5d3SJohn Marino     }
158*86d7f5d3SJohn Marino     else
159*86d7f5d3SJohn Marino     {
160*86d7f5d3SJohn Marino 	/* It is an unrecognized character.  We return an error to
161*86d7f5d3SJohn Marino 	   reserve these for future versions of CVS; it is plausible
162*86d7f5d3SJohn Marino 	   that various crazy syntaxes might be invented for inserting
163*86d7f5d3SJohn Marino 	   information about revisions, branches, etc.  */
164*86d7f5d3SJohn Marino 	if (line)
165*86d7f5d3SJohn Marino 	    error (0, 0, "%s:%d: unrecognized variable syntax %s",
166*86d7f5d3SJohn Marino 		   file, line, name);
167*86d7f5d3SJohn Marino 	else
168*86d7f5d3SJohn Marino 	    error (0, 0, "%s: unrecognized variable syntax %s",
169*86d7f5d3SJohn Marino 		   file, name);
170*86d7f5d3SJohn Marino 	return NULL;
171*86d7f5d3SJohn Marino     }
172*86d7f5d3SJohn Marino }
173*86d7f5d3SJohn Marino 
174*86d7f5d3SJohn Marino 
175*86d7f5d3SJohn Marino 
176*86d7f5d3SJohn Marino /* This routine will expand the pathname to account for ~ and $
177*86d7f5d3SJohn Marino  * characters as described above.  Returns a pointer to a newly
178*86d7f5d3SJohn Marino  * malloc'd string.  If an error occurs, an error message is printed
179*86d7f5d3SJohn Marino  * via error() and NULL is returned.  FILE and LINE are the filename
180*86d7f5d3SJohn Marino  * and linenumber to include in the error message.  FILE must point
181*86d7f5d3SJohn Marino  * to something; LINE can be zero to indicate the line number is not
182*86d7f5d3SJohn Marino  * known.
183*86d7f5d3SJohn Marino  *
184*86d7f5d3SJohn Marino  * When FORMATSAFE is set, percent signs (`%') in variable contents are doubled
185*86d7f5d3SJohn Marino  * to prevent later expansion by format_cmdline.
186*86d7f5d3SJohn Marino  *
187*86d7f5d3SJohn Marino  * CVSROOT is used to expanding $CVSROOT.
188*86d7f5d3SJohn Marino  */
189*86d7f5d3SJohn Marino char *
expand_path(const char * name,const char * cvsroot,bool formatsafe,const char * file,int line)190*86d7f5d3SJohn Marino expand_path (const char *name, const char *cvsroot, bool formatsafe,
191*86d7f5d3SJohn Marino 	     const char *file, int line)
192*86d7f5d3SJohn Marino {
193*86d7f5d3SJohn Marino     size_t s, d, p;
194*86d7f5d3SJohn Marino     const char *e;
195*86d7f5d3SJohn Marino 
196*86d7f5d3SJohn Marino     char *mybuf = NULL;
197*86d7f5d3SJohn Marino     size_t mybuf_size = 0;
198*86d7f5d3SJohn Marino     char *buf = NULL;
199*86d7f5d3SJohn Marino     size_t buf_size = 0;
200*86d7f5d3SJohn Marino 
201*86d7f5d3SJohn Marino     char inquotes = '\0';
202*86d7f5d3SJohn Marino 
203*86d7f5d3SJohn Marino     char *result;
204*86d7f5d3SJohn Marino 
205*86d7f5d3SJohn Marino     /* Sorry this routine is so ugly; it is a head-on collision
206*86d7f5d3SJohn Marino        between the `traditional' unix *d++ style and the need to
207*86d7f5d3SJohn Marino        dynamically allocate.  It would be much cleaner (and probably
208*86d7f5d3SJohn Marino        faster, not that this is a bottleneck for CVS) with more use of
209*86d7f5d3SJohn Marino        strcpy & friends, but I haven't taken the effort to rewrite it
210*86d7f5d3SJohn Marino        thusly.  */
211*86d7f5d3SJohn Marino 
212*86d7f5d3SJohn Marino     /* First copy from NAME to MYBUF, expanding $<foo> as we go.  */
213*86d7f5d3SJohn Marino     s = d = 0;
214*86d7f5d3SJohn Marino     expand_string (&mybuf, &mybuf_size, d + 1);
215*86d7f5d3SJohn Marino     while ((mybuf[d++] = name[s]) != '\0')
216*86d7f5d3SJohn Marino     {
217*86d7f5d3SJohn Marino 	if (name[s] == '\\')
218*86d7f5d3SJohn Marino 	{
219*86d7f5d3SJohn Marino 	    /* The next character is a literal.  Leave the \ in the string
220*86d7f5d3SJohn Marino 	     * since it will be needed again when the string is split into
221*86d7f5d3SJohn Marino 	     * arguments.
222*86d7f5d3SJohn Marino 	     */
223*86d7f5d3SJohn Marino 	    /* if we have a \ as the last character of the string, just leave
224*86d7f5d3SJohn Marino 	     * it there - this is where we would set the escape flag to tell
225*86d7f5d3SJohn Marino 	     * our parent we want another line if we cared.
226*86d7f5d3SJohn Marino 	     */
227*86d7f5d3SJohn Marino 	    if (name[++s])
228*86d7f5d3SJohn Marino 	    {
229*86d7f5d3SJohn Marino 		expand_string (&mybuf, &mybuf_size, d + 1);
230*86d7f5d3SJohn Marino 		mybuf[d++] = name[s++];
231*86d7f5d3SJohn Marino 	    }
232*86d7f5d3SJohn Marino 	}
233*86d7f5d3SJohn Marino 	/* skip $ variable processing for text inside single quotes */
234*86d7f5d3SJohn Marino 	else if (inquotes == '\'')
235*86d7f5d3SJohn Marino 	{
236*86d7f5d3SJohn Marino 	    if (name[s++] == '\'')
237*86d7f5d3SJohn Marino 	    {
238*86d7f5d3SJohn Marino 		inquotes = '\0';
239*86d7f5d3SJohn Marino 	    }
240*86d7f5d3SJohn Marino 	}
241*86d7f5d3SJohn Marino 	else if (name[s] == '\'')
242*86d7f5d3SJohn Marino 	{
243*86d7f5d3SJohn Marino 	    s++;
244*86d7f5d3SJohn Marino 	    inquotes = '\'';
245*86d7f5d3SJohn Marino 	}
246*86d7f5d3SJohn Marino 	else if (name[s] == '"')
247*86d7f5d3SJohn Marino 	{
248*86d7f5d3SJohn Marino 	    s++;
249*86d7f5d3SJohn Marino 	    if (inquotes) inquotes = '\0';
250*86d7f5d3SJohn Marino 	    else inquotes = '"';
251*86d7f5d3SJohn Marino 	}
252*86d7f5d3SJohn Marino 	else if (name[s++] == '$')
253*86d7f5d3SJohn Marino 	{
254*86d7f5d3SJohn Marino 	    int flag = (name[s] == '{');
255*86d7f5d3SJohn Marino 	    p = d;
256*86d7f5d3SJohn Marino 
257*86d7f5d3SJohn Marino 	    expand_string (&mybuf, &mybuf_size, d + 1);
258*86d7f5d3SJohn Marino 	    for (; (mybuf[d++] = name[s]); s++)
259*86d7f5d3SJohn Marino 	    {
260*86d7f5d3SJohn Marino 		if (flag
261*86d7f5d3SJohn Marino 		    ? name[s] =='}'
262*86d7f5d3SJohn Marino 		    : !isalnum (name[s]) && name[s] != '_')
263*86d7f5d3SJohn Marino 		    break;
264*86d7f5d3SJohn Marino 		expand_string (&mybuf, &mybuf_size, d + 1);
265*86d7f5d3SJohn Marino 	    }
266*86d7f5d3SJohn Marino 	    mybuf[--d] = '\0';
267*86d7f5d3SJohn Marino 	    e = expand_variable (&mybuf[p+flag], cvsroot, file, line);
268*86d7f5d3SJohn Marino 
269*86d7f5d3SJohn Marino 	    if (e)
270*86d7f5d3SJohn Marino 	    {
271*86d7f5d3SJohn Marino 		expand_string (&mybuf, &mybuf_size, d + 1);
272*86d7f5d3SJohn Marino 		for (d = p - 1; (mybuf[d++] = *e++); )
273*86d7f5d3SJohn Marino 		{
274*86d7f5d3SJohn Marino 		    expand_string (&mybuf, &mybuf_size, d + 1);
275*86d7f5d3SJohn Marino 		    if (mybuf[d-1] == '"')
276*86d7f5d3SJohn Marino 		    {
277*86d7f5d3SJohn Marino 			/* escape the double quotes if we're between a matched
278*86d7f5d3SJohn Marino 			 * pair of double quotes so that this sub will be
279*86d7f5d3SJohn Marino 			 * passed inside as or as part of a single argument
280*86d7f5d3SJohn Marino 			 * during the argument split later.
281*86d7f5d3SJohn Marino 			 */
282*86d7f5d3SJohn Marino 			if (inquotes)
283*86d7f5d3SJohn Marino 			{
284*86d7f5d3SJohn Marino 			    mybuf[d-1] = '\\';
285*86d7f5d3SJohn Marino 			    expand_string (&mybuf, &mybuf_size, d + 1);
286*86d7f5d3SJohn Marino 			    mybuf[d++] = '"';
287*86d7f5d3SJohn Marino 			}
288*86d7f5d3SJohn Marino 		    }
289*86d7f5d3SJohn Marino 		    else if (formatsafe && mybuf[d-1] == '%')
290*86d7f5d3SJohn Marino 		    {
291*86d7f5d3SJohn Marino 			/* escape '%' to get past printf style format strings
292*86d7f5d3SJohn Marino 			 * later (in make_cmdline).
293*86d7f5d3SJohn Marino 			 */
294*86d7f5d3SJohn Marino 			expand_string (&mybuf, &mybuf_size, d + 1);
295*86d7f5d3SJohn Marino 			mybuf[d] = '%';
296*86d7f5d3SJohn Marino 			d++;
297*86d7f5d3SJohn Marino 		    }
298*86d7f5d3SJohn Marino 		}
299*86d7f5d3SJohn Marino 		--d;
300*86d7f5d3SJohn Marino 		if (flag && name[s])
301*86d7f5d3SJohn Marino 		    s++;
302*86d7f5d3SJohn Marino 	    }
303*86d7f5d3SJohn Marino 	    else
304*86d7f5d3SJohn Marino 		/* expand_variable has already printed an error message.  */
305*86d7f5d3SJohn Marino 		goto error_exit;
306*86d7f5d3SJohn Marino 	}
307*86d7f5d3SJohn Marino 	expand_string (&mybuf, &mybuf_size, d + 1);
308*86d7f5d3SJohn Marino     }
309*86d7f5d3SJohn Marino     expand_string (&mybuf, &mybuf_size, d + 1);
310*86d7f5d3SJohn Marino     mybuf[d] = '\0';
311*86d7f5d3SJohn Marino 
312*86d7f5d3SJohn Marino     /* Then copy from MYBUF to BUF, expanding ~.  */
313*86d7f5d3SJohn Marino     s = d = 0;
314*86d7f5d3SJohn Marino     /* If you don't want ~username ~/ to be expanded simply remove
315*86d7f5d3SJohn Marino      * This entire if statement including the else portion
316*86d7f5d3SJohn Marino      */
317*86d7f5d3SJohn Marino     if (mybuf[s] == '~')
318*86d7f5d3SJohn Marino     {
319*86d7f5d3SJohn Marino 	p = d;
320*86d7f5d3SJohn Marino 	while (mybuf[++s] != '/' && mybuf[s] != '\0')
321*86d7f5d3SJohn Marino 	{
322*86d7f5d3SJohn Marino 	    expand_string (&buf, &buf_size, p + 1);
323*86d7f5d3SJohn Marino 	    buf[p++] = name[s];
324*86d7f5d3SJohn Marino 	}
325*86d7f5d3SJohn Marino 	expand_string (&buf, &buf_size, p + 1);
326*86d7f5d3SJohn Marino 	buf[p] = '\0';
327*86d7f5d3SJohn Marino 
328*86d7f5d3SJohn Marino 	if (p == d)
329*86d7f5d3SJohn Marino 	    e = get_homedir ();
330*86d7f5d3SJohn Marino 	else
331*86d7f5d3SJohn Marino 	{
332*86d7f5d3SJohn Marino #ifdef GETPWNAM_MISSING
333*86d7f5d3SJohn Marino 	    if (line)
334*86d7f5d3SJohn Marino 		error (0, 0,
335*86d7f5d3SJohn Marino 		       "%s:%d:tilde expansion not supported on this system",
336*86d7f5d3SJohn Marino 		       file, line);
337*86d7f5d3SJohn Marino 	    else
338*86d7f5d3SJohn Marino 		error (0, 0, "%s:tilde expansion not supported on this system",
339*86d7f5d3SJohn Marino 		       file);
340*86d7f5d3SJohn Marino 	    goto error_exit;
341*86d7f5d3SJohn Marino #else
342*86d7f5d3SJohn Marino 	    struct passwd *ps;
343*86d7f5d3SJohn Marino 	    ps = getpwnam (buf + d);
344*86d7f5d3SJohn Marino 	    if (ps == NULL)
345*86d7f5d3SJohn Marino 	    {
346*86d7f5d3SJohn Marino 		if (line)
347*86d7f5d3SJohn Marino 		    error (0, 0, "%s:%d: no such user %s",
348*86d7f5d3SJohn Marino 			   file, line, buf + d);
349*86d7f5d3SJohn Marino 		else
350*86d7f5d3SJohn Marino 		    error (0, 0, "%s: no such user %s", file, buf + d);
351*86d7f5d3SJohn Marino 		goto error_exit;
352*86d7f5d3SJohn Marino 	    }
353*86d7f5d3SJohn Marino 	    e = ps->pw_dir;
354*86d7f5d3SJohn Marino #endif
355*86d7f5d3SJohn Marino 	}
356*86d7f5d3SJohn Marino 	if (!e)
357*86d7f5d3SJohn Marino 	    error (1, 0, "cannot find home directory");
358*86d7f5d3SJohn Marino 
359*86d7f5d3SJohn Marino 	p = strlen (e);
360*86d7f5d3SJohn Marino 	expand_string (&buf, &buf_size, d + p);
361*86d7f5d3SJohn Marino 	memcpy (buf + d, e, p);
362*86d7f5d3SJohn Marino 	d += p;
363*86d7f5d3SJohn Marino     }
364*86d7f5d3SJohn Marino     /* Kill up to here */
365*86d7f5d3SJohn Marino     p = strlen (mybuf + s) + 1;
366*86d7f5d3SJohn Marino     expand_string (&buf, &buf_size, d + p);
367*86d7f5d3SJohn Marino     memcpy (buf + d, mybuf + s, p);
368*86d7f5d3SJohn Marino 
369*86d7f5d3SJohn Marino     /* OK, buf contains the value we want to return.  Clean up and return
370*86d7f5d3SJohn Marino        it.  */
371*86d7f5d3SJohn Marino     free (mybuf);
372*86d7f5d3SJohn Marino     /* Save a little memory with xstrdup; buf will tend to allocate
373*86d7f5d3SJohn Marino        more than it needs to.  */
374*86d7f5d3SJohn Marino     result = xstrdup (buf);
375*86d7f5d3SJohn Marino     free (buf);
376*86d7f5d3SJohn Marino     return result;
377*86d7f5d3SJohn Marino 
378*86d7f5d3SJohn Marino  error_exit:
379*86d7f5d3SJohn Marino     if (mybuf) free (mybuf);
380*86d7f5d3SJohn Marino     if (buf) free (buf);
381*86d7f5d3SJohn Marino     return NULL;
382*86d7f5d3SJohn Marino }
383