xref: /dflybsd-src/contrib/cvs-1.12/src/expand_path.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
186d7f5d3SJohn Marino /* expand_path.c -- expand environmental variables in passed in string
286d7f5d3SJohn Marino  *
386d7f5d3SJohn Marino  * Copyright (C) 1995-2005 The Free Software Foundation, Inc.
486d7f5d3SJohn Marino  *
586d7f5d3SJohn Marino  * This program is free software; you can redistribute it and/or modify
686d7f5d3SJohn Marino  * it under the terms of the GNU General Public License as published by
786d7f5d3SJohn Marino  * the Free Software Foundation; either version 2, or (at your option)
886d7f5d3SJohn Marino  * any later version.
986d7f5d3SJohn Marino  *
1086d7f5d3SJohn Marino  * This program is distributed in the hope that it will be useful,
1186d7f5d3SJohn Marino  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1286d7f5d3SJohn Marino  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1386d7f5d3SJohn Marino  * GNU General Public License for more details.
1486d7f5d3SJohn Marino  *
1586d7f5d3SJohn Marino  * The main routine is expand_path(), it is the routine that handles
1686d7f5d3SJohn Marino  * the '~' character in four forms:
1786d7f5d3SJohn Marino  *     ~name
1886d7f5d3SJohn Marino  *     ~name/
1986d7f5d3SJohn Marino  *     ~/
2086d7f5d3SJohn Marino  *     ~
2186d7f5d3SJohn Marino  * and handles environment variables contained within the pathname
2286d7f5d3SJohn Marino  * which are defined by:
2386d7f5d3SJohn Marino  *     ${var_name}   (var_name is the name of the environ variable)
2486d7f5d3SJohn Marino  *     $var_name     (var_name ends w/ non-alphanumeric char other than '_')
2586d7f5d3SJohn Marino  */
2686d7f5d3SJohn Marino 
2786d7f5d3SJohn Marino #include "cvs.h"
2886d7f5d3SJohn Marino #include <sys/types.h>
2986d7f5d3SJohn Marino 
3086d7f5d3SJohn Marino /* User variables.  */
3186d7f5d3SJohn Marino 
3286d7f5d3SJohn Marino List *variable_list;
3386d7f5d3SJohn Marino 
3486d7f5d3SJohn Marino static void variable_delproc (Node *);
3586d7f5d3SJohn Marino 
3686d7f5d3SJohn Marino static void
variable_delproc(Node * node)3786d7f5d3SJohn Marino variable_delproc (Node *node)
3886d7f5d3SJohn Marino {
3986d7f5d3SJohn Marino     free (node->data);
4086d7f5d3SJohn Marino }
4186d7f5d3SJohn Marino 
4286d7f5d3SJohn Marino /* Currently used by -s option; we might want a way to set user
4386d7f5d3SJohn Marino    variables in a file in the $CVSROOT/CVSROOT directory too.  */
4486d7f5d3SJohn Marino 
4586d7f5d3SJohn Marino void
variable_set(char * nameval)4686d7f5d3SJohn Marino variable_set (char *nameval)
4786d7f5d3SJohn Marino {
4886d7f5d3SJohn Marino     char *p;
4986d7f5d3SJohn Marino     char *name;
5086d7f5d3SJohn Marino     Node *node;
5186d7f5d3SJohn Marino 
5286d7f5d3SJohn Marino     p = nameval;
5386d7f5d3SJohn Marino     while (isalnum ((unsigned char) *p) || *p == '_')
5486d7f5d3SJohn Marino 	++p;
5586d7f5d3SJohn Marino     if (*p != '=')
5686d7f5d3SJohn Marino 	error (1, 0, "invalid character in user variable name in %s", nameval);
5786d7f5d3SJohn Marino     if (p == nameval)
5886d7f5d3SJohn Marino 	error (1, 0, "empty user variable name in %s", nameval);
5986d7f5d3SJohn Marino     name = xmalloc (p - nameval + 1);
6086d7f5d3SJohn Marino     strncpy (name, nameval, p - nameval);
6186d7f5d3SJohn Marino     name[p - nameval] = '\0';
6286d7f5d3SJohn Marino     /* Make p point to the value.  */
6386d7f5d3SJohn Marino     ++p;
6486d7f5d3SJohn Marino     if (strchr (p, '\012'))
6586d7f5d3SJohn Marino 	error (1, 0, "linefeed in user variable value in %s", nameval);
6686d7f5d3SJohn Marino 
6786d7f5d3SJohn Marino     if (!variable_list)
6886d7f5d3SJohn Marino 	variable_list = getlist ();
6986d7f5d3SJohn Marino 
7086d7f5d3SJohn Marino     node = findnode (variable_list, name);
7186d7f5d3SJohn Marino     if (!node)
7286d7f5d3SJohn Marino     {
7386d7f5d3SJohn Marino 	node = getnode ();
7486d7f5d3SJohn Marino 	node->type = VARIABLE;
7586d7f5d3SJohn Marino 	node->delproc = variable_delproc;
7686d7f5d3SJohn Marino 	node->key = name;
7786d7f5d3SJohn Marino 	node->data = xstrdup (p);
7886d7f5d3SJohn Marino 	(void) addnode (variable_list, node);
7986d7f5d3SJohn Marino     }
8086d7f5d3SJohn Marino     else
8186d7f5d3SJohn Marino     {
8286d7f5d3SJohn Marino 	/* Replace the old value.  For example, this means that -s
8386d7f5d3SJohn Marino 	   options on the command line override ones from .cvsrc.  */
8486d7f5d3SJohn Marino 	free (node->data);
8586d7f5d3SJohn Marino 	node->data = xstrdup (p);
8686d7f5d3SJohn Marino 	free (name);
8786d7f5d3SJohn Marino     }
8886d7f5d3SJohn Marino }
8986d7f5d3SJohn Marino 
9086d7f5d3SJohn Marino 
9186d7f5d3SJohn Marino 
9286d7f5d3SJohn Marino /* Expand variable NAME into its contents, per the rules above.
9386d7f5d3SJohn Marino  *
9486d7f5d3SJohn Marino  * CVSROOT is used to expanding $CVSROOT.
9586d7f5d3SJohn Marino  *
9686d7f5d3SJohn Marino  * RETURNS
9786d7f5d3SJohn Marino  *   A pointer to the requested variable contents or NULL when the requested
9886d7f5d3SJohn Marino  *   variable is not found.
9986d7f5d3SJohn Marino  *
10086d7f5d3SJohn Marino  * ERRORS
10186d7f5d3SJohn Marino  *   None, though this function may generate warning messages when NAME is not
10286d7f5d3SJohn Marino  *   found.
10386d7f5d3SJohn Marino  */
10486d7f5d3SJohn Marino static const char *
expand_variable(const char * name,const char * cvsroot,const char * file,int line)10586d7f5d3SJohn Marino expand_variable (const char *name, const char *cvsroot,
10686d7f5d3SJohn Marino 		 const char *file, int line)
10786d7f5d3SJohn Marino {
10886d7f5d3SJohn Marino     if (!strcmp (name, CVSROOT_ENV))
10986d7f5d3SJohn Marino 	return cvsroot;
11086d7f5d3SJohn Marino     else if (!strcmp (name, "RCSBIN"))
11186d7f5d3SJohn Marino     {
11286d7f5d3SJohn Marino 	error (0, 0, "RCSBIN internal variable is no longer supported");
11386d7f5d3SJohn Marino 	return NULL;
11486d7f5d3SJohn Marino     }
11586d7f5d3SJohn Marino     else if (!strcmp (name, EDITOR1_ENV))
11686d7f5d3SJohn Marino 	return Editor;
11786d7f5d3SJohn Marino     else if (!strcmp (name, EDITOR2_ENV))
11886d7f5d3SJohn Marino 	return Editor;
11986d7f5d3SJohn Marino     else if (!strcmp (name, EDITOR3_ENV))
12086d7f5d3SJohn Marino 	return Editor;
12186d7f5d3SJohn Marino     else if (!strcmp (name, "USER"))
12286d7f5d3SJohn Marino 	return getcaller ();
12386d7f5d3SJohn Marino     else if (!strcmp (name, "SESSIONID")
12486d7f5d3SJohn Marino 	     || !strcmp (name, "COMMITID"))
12586d7f5d3SJohn Marino 	return global_session_id;
12686d7f5d3SJohn Marino     else if (isalpha (name[0]))
12786d7f5d3SJohn Marino     {
12886d7f5d3SJohn Marino 	/* These names are reserved for future versions of CVS,
12986d7f5d3SJohn Marino 	   so that is why it is an error.  */
13086d7f5d3SJohn Marino 	if (line)
13186d7f5d3SJohn Marino 	    error (0, 0, "%s:%d: no such internal variable $%s",
13286d7f5d3SJohn Marino 		   file, line, name);
13386d7f5d3SJohn Marino 	else
13486d7f5d3SJohn Marino 	    error (0, 0, "%s: no such internal variable $%s",
13586d7f5d3SJohn Marino 		   file, name);
13686d7f5d3SJohn Marino 	return NULL;
13786d7f5d3SJohn Marino     }
13886d7f5d3SJohn Marino     else if (name[0] == '=')
13986d7f5d3SJohn Marino     {
14086d7f5d3SJohn Marino 	Node *node;
14186d7f5d3SJohn Marino 	/* Crazy syntax for a user variable.  But we want
14286d7f5d3SJohn Marino 	   *something* that lets the user name a user variable
14386d7f5d3SJohn Marino 	   anything he wants, without interference from
14486d7f5d3SJohn Marino 	   (existing or future) internal variables.  */
14586d7f5d3SJohn Marino 	node = findnode (variable_list, name + 1);
14686d7f5d3SJohn Marino 	if (!node)
14786d7f5d3SJohn Marino 	{
14886d7f5d3SJohn Marino 	    if (line)
14986d7f5d3SJohn Marino 		error (0, 0, "%s:%d: no such user variable ${%s}",
15086d7f5d3SJohn Marino 		       file, line, name);
15186d7f5d3SJohn Marino 	    else
15286d7f5d3SJohn Marino 		error (0, 0, "%s: no such user variable ${%s}",
15386d7f5d3SJohn Marino 		       file, name);
15486d7f5d3SJohn Marino 	    return NULL;
15586d7f5d3SJohn Marino 	}
15686d7f5d3SJohn Marino 	return node->data;
15786d7f5d3SJohn Marino     }
15886d7f5d3SJohn Marino     else
15986d7f5d3SJohn Marino     {
16086d7f5d3SJohn Marino 	/* It is an unrecognized character.  We return an error to
16186d7f5d3SJohn Marino 	   reserve these for future versions of CVS; it is plausible
16286d7f5d3SJohn Marino 	   that various crazy syntaxes might be invented for inserting
16386d7f5d3SJohn Marino 	   information about revisions, branches, etc.  */
16486d7f5d3SJohn Marino 	if (line)
16586d7f5d3SJohn Marino 	    error (0, 0, "%s:%d: unrecognized variable syntax %s",
16686d7f5d3SJohn Marino 		   file, line, name);
16786d7f5d3SJohn Marino 	else
16886d7f5d3SJohn Marino 	    error (0, 0, "%s: unrecognized variable syntax %s",
16986d7f5d3SJohn Marino 		   file, name);
17086d7f5d3SJohn Marino 	return NULL;
17186d7f5d3SJohn Marino     }
17286d7f5d3SJohn Marino }
17386d7f5d3SJohn Marino 
17486d7f5d3SJohn Marino 
17586d7f5d3SJohn Marino 
17686d7f5d3SJohn Marino /* This routine will expand the pathname to account for ~ and $
17786d7f5d3SJohn Marino  * characters as described above.  Returns a pointer to a newly
17886d7f5d3SJohn Marino  * malloc'd string.  If an error occurs, an error message is printed
17986d7f5d3SJohn Marino  * via error() and NULL is returned.  FILE and LINE are the filename
18086d7f5d3SJohn Marino  * and linenumber to include in the error message.  FILE must point
18186d7f5d3SJohn Marino  * to something; LINE can be zero to indicate the line number is not
18286d7f5d3SJohn Marino  * known.
18386d7f5d3SJohn Marino  *
18486d7f5d3SJohn Marino  * When FORMATSAFE is set, percent signs (`%') in variable contents are doubled
18586d7f5d3SJohn Marino  * to prevent later expansion by format_cmdline.
18686d7f5d3SJohn Marino  *
18786d7f5d3SJohn Marino  * CVSROOT is used to expanding $CVSROOT.
18886d7f5d3SJohn Marino  */
18986d7f5d3SJohn Marino char *
expand_path(const char * name,const char * cvsroot,bool formatsafe,const char * file,int line)19086d7f5d3SJohn Marino expand_path (const char *name, const char *cvsroot, bool formatsafe,
19186d7f5d3SJohn Marino 	     const char *file, int line)
19286d7f5d3SJohn Marino {
19386d7f5d3SJohn Marino     size_t s, d, p;
19486d7f5d3SJohn Marino     const char *e;
19586d7f5d3SJohn Marino 
19686d7f5d3SJohn Marino     char *mybuf = NULL;
19786d7f5d3SJohn Marino     size_t mybuf_size = 0;
19886d7f5d3SJohn Marino     char *buf = NULL;
19986d7f5d3SJohn Marino     size_t buf_size = 0;
20086d7f5d3SJohn Marino 
20186d7f5d3SJohn Marino     char inquotes = '\0';
20286d7f5d3SJohn Marino 
20386d7f5d3SJohn Marino     char *result;
20486d7f5d3SJohn Marino 
20586d7f5d3SJohn Marino     /* Sorry this routine is so ugly; it is a head-on collision
20686d7f5d3SJohn Marino        between the `traditional' unix *d++ style and the need to
20786d7f5d3SJohn Marino        dynamically allocate.  It would be much cleaner (and probably
20886d7f5d3SJohn Marino        faster, not that this is a bottleneck for CVS) with more use of
20986d7f5d3SJohn Marino        strcpy & friends, but I haven't taken the effort to rewrite it
21086d7f5d3SJohn Marino        thusly.  */
21186d7f5d3SJohn Marino 
21286d7f5d3SJohn Marino     /* First copy from NAME to MYBUF, expanding $<foo> as we go.  */
21386d7f5d3SJohn Marino     s = d = 0;
21486d7f5d3SJohn Marino     expand_string (&mybuf, &mybuf_size, d + 1);
21586d7f5d3SJohn Marino     while ((mybuf[d++] = name[s]) != '\0')
21686d7f5d3SJohn Marino     {
21786d7f5d3SJohn Marino 	if (name[s] == '\\')
21886d7f5d3SJohn Marino 	{
21986d7f5d3SJohn Marino 	    /* The next character is a literal.  Leave the \ in the string
22086d7f5d3SJohn Marino 	     * since it will be needed again when the string is split into
22186d7f5d3SJohn Marino 	     * arguments.
22286d7f5d3SJohn Marino 	     */
22386d7f5d3SJohn Marino 	    /* if we have a \ as the last character of the string, just leave
22486d7f5d3SJohn Marino 	     * it there - this is where we would set the escape flag to tell
22586d7f5d3SJohn Marino 	     * our parent we want another line if we cared.
22686d7f5d3SJohn Marino 	     */
22786d7f5d3SJohn Marino 	    if (name[++s])
22886d7f5d3SJohn Marino 	    {
22986d7f5d3SJohn Marino 		expand_string (&mybuf, &mybuf_size, d + 1);
23086d7f5d3SJohn Marino 		mybuf[d++] = name[s++];
23186d7f5d3SJohn Marino 	    }
23286d7f5d3SJohn Marino 	}
23386d7f5d3SJohn Marino 	/* skip $ variable processing for text inside single quotes */
23486d7f5d3SJohn Marino 	else if (inquotes == '\'')
23586d7f5d3SJohn Marino 	{
23686d7f5d3SJohn Marino 	    if (name[s++] == '\'')
23786d7f5d3SJohn Marino 	    {
23886d7f5d3SJohn Marino 		inquotes = '\0';
23986d7f5d3SJohn Marino 	    }
24086d7f5d3SJohn Marino 	}
24186d7f5d3SJohn Marino 	else if (name[s] == '\'')
24286d7f5d3SJohn Marino 	{
24386d7f5d3SJohn Marino 	    s++;
24486d7f5d3SJohn Marino 	    inquotes = '\'';
24586d7f5d3SJohn Marino 	}
24686d7f5d3SJohn Marino 	else if (name[s] == '"')
24786d7f5d3SJohn Marino 	{
24886d7f5d3SJohn Marino 	    s++;
24986d7f5d3SJohn Marino 	    if (inquotes) inquotes = '\0';
25086d7f5d3SJohn Marino 	    else inquotes = '"';
25186d7f5d3SJohn Marino 	}
25286d7f5d3SJohn Marino 	else if (name[s++] == '$')
25386d7f5d3SJohn Marino 	{
25486d7f5d3SJohn Marino 	    int flag = (name[s] == '{');
25586d7f5d3SJohn Marino 	    p = d;
25686d7f5d3SJohn Marino 
25786d7f5d3SJohn Marino 	    expand_string (&mybuf, &mybuf_size, d + 1);
25886d7f5d3SJohn Marino 	    for (; (mybuf[d++] = name[s]); s++)
25986d7f5d3SJohn Marino 	    {
26086d7f5d3SJohn Marino 		if (flag
26186d7f5d3SJohn Marino 		    ? name[s] =='}'
26286d7f5d3SJohn Marino 		    : !isalnum (name[s]) && name[s] != '_')
26386d7f5d3SJohn Marino 		    break;
26486d7f5d3SJohn Marino 		expand_string (&mybuf, &mybuf_size, d + 1);
26586d7f5d3SJohn Marino 	    }
26686d7f5d3SJohn Marino 	    mybuf[--d] = '\0';
26786d7f5d3SJohn Marino 	    e = expand_variable (&mybuf[p+flag], cvsroot, file, line);
26886d7f5d3SJohn Marino 
26986d7f5d3SJohn Marino 	    if (e)
27086d7f5d3SJohn Marino 	    {
27186d7f5d3SJohn Marino 		expand_string (&mybuf, &mybuf_size, d + 1);
27286d7f5d3SJohn Marino 		for (d = p - 1; (mybuf[d++] = *e++); )
27386d7f5d3SJohn Marino 		{
27486d7f5d3SJohn Marino 		    expand_string (&mybuf, &mybuf_size, d + 1);
27586d7f5d3SJohn Marino 		    if (mybuf[d-1] == '"')
27686d7f5d3SJohn Marino 		    {
27786d7f5d3SJohn Marino 			/* escape the double quotes if we're between a matched
27886d7f5d3SJohn Marino 			 * pair of double quotes so that this sub will be
27986d7f5d3SJohn Marino 			 * passed inside as or as part of a single argument
28086d7f5d3SJohn Marino 			 * during the argument split later.
28186d7f5d3SJohn Marino 			 */
28286d7f5d3SJohn Marino 			if (inquotes)
28386d7f5d3SJohn Marino 			{
28486d7f5d3SJohn Marino 			    mybuf[d-1] = '\\';
28586d7f5d3SJohn Marino 			    expand_string (&mybuf, &mybuf_size, d + 1);
28686d7f5d3SJohn Marino 			    mybuf[d++] = '"';
28786d7f5d3SJohn Marino 			}
28886d7f5d3SJohn Marino 		    }
28986d7f5d3SJohn Marino 		    else if (formatsafe && mybuf[d-1] == '%')
29086d7f5d3SJohn Marino 		    {
29186d7f5d3SJohn Marino 			/* escape '%' to get past printf style format strings
29286d7f5d3SJohn Marino 			 * later (in make_cmdline).
29386d7f5d3SJohn Marino 			 */
29486d7f5d3SJohn Marino 			expand_string (&mybuf, &mybuf_size, d + 1);
29586d7f5d3SJohn Marino 			mybuf[d] = '%';
29686d7f5d3SJohn Marino 			d++;
29786d7f5d3SJohn Marino 		    }
29886d7f5d3SJohn Marino 		}
29986d7f5d3SJohn Marino 		--d;
30086d7f5d3SJohn Marino 		if (flag && name[s])
30186d7f5d3SJohn Marino 		    s++;
30286d7f5d3SJohn Marino 	    }
30386d7f5d3SJohn Marino 	    else
30486d7f5d3SJohn Marino 		/* expand_variable has already printed an error message.  */
30586d7f5d3SJohn Marino 		goto error_exit;
30686d7f5d3SJohn Marino 	}
30786d7f5d3SJohn Marino 	expand_string (&mybuf, &mybuf_size, d + 1);
30886d7f5d3SJohn Marino     }
30986d7f5d3SJohn Marino     expand_string (&mybuf, &mybuf_size, d + 1);
31086d7f5d3SJohn Marino     mybuf[d] = '\0';
31186d7f5d3SJohn Marino 
31286d7f5d3SJohn Marino     /* Then copy from MYBUF to BUF, expanding ~.  */
31386d7f5d3SJohn Marino     s = d = 0;
31486d7f5d3SJohn Marino     /* If you don't want ~username ~/ to be expanded simply remove
31586d7f5d3SJohn Marino      * This entire if statement including the else portion
31686d7f5d3SJohn Marino      */
31786d7f5d3SJohn Marino     if (mybuf[s] == '~')
31886d7f5d3SJohn Marino     {
31986d7f5d3SJohn Marino 	p = d;
32086d7f5d3SJohn Marino 	while (mybuf[++s] != '/' && mybuf[s] != '\0')
32186d7f5d3SJohn Marino 	{
32286d7f5d3SJohn Marino 	    expand_string (&buf, &buf_size, p + 1);
32386d7f5d3SJohn Marino 	    buf[p++] = name[s];
32486d7f5d3SJohn Marino 	}
32586d7f5d3SJohn Marino 	expand_string (&buf, &buf_size, p + 1);
32686d7f5d3SJohn Marino 	buf[p] = '\0';
32786d7f5d3SJohn Marino 
32886d7f5d3SJohn Marino 	if (p == d)
32986d7f5d3SJohn Marino 	    e = get_homedir ();
33086d7f5d3SJohn Marino 	else
33186d7f5d3SJohn Marino 	{
33286d7f5d3SJohn Marino #ifdef GETPWNAM_MISSING
33386d7f5d3SJohn Marino 	    if (line)
33486d7f5d3SJohn Marino 		error (0, 0,
33586d7f5d3SJohn Marino 		       "%s:%d:tilde expansion not supported on this system",
33686d7f5d3SJohn Marino 		       file, line);
33786d7f5d3SJohn Marino 	    else
33886d7f5d3SJohn Marino 		error (0, 0, "%s:tilde expansion not supported on this system",
33986d7f5d3SJohn Marino 		       file);
34086d7f5d3SJohn Marino 	    goto error_exit;
34186d7f5d3SJohn Marino #else
34286d7f5d3SJohn Marino 	    struct passwd *ps;
34386d7f5d3SJohn Marino 	    ps = getpwnam (buf + d);
34486d7f5d3SJohn Marino 	    if (ps == NULL)
34586d7f5d3SJohn Marino 	    {
34686d7f5d3SJohn Marino 		if (line)
34786d7f5d3SJohn Marino 		    error (0, 0, "%s:%d: no such user %s",
34886d7f5d3SJohn Marino 			   file, line, buf + d);
34986d7f5d3SJohn Marino 		else
35086d7f5d3SJohn Marino 		    error (0, 0, "%s: no such user %s", file, buf + d);
35186d7f5d3SJohn Marino 		goto error_exit;
35286d7f5d3SJohn Marino 	    }
35386d7f5d3SJohn Marino 	    e = ps->pw_dir;
35486d7f5d3SJohn Marino #endif
35586d7f5d3SJohn Marino 	}
35686d7f5d3SJohn Marino 	if (!e)
35786d7f5d3SJohn Marino 	    error (1, 0, "cannot find home directory");
35886d7f5d3SJohn Marino 
35986d7f5d3SJohn Marino 	p = strlen (e);
36086d7f5d3SJohn Marino 	expand_string (&buf, &buf_size, d + p);
36186d7f5d3SJohn Marino 	memcpy (buf + d, e, p);
36286d7f5d3SJohn Marino 	d += p;
36386d7f5d3SJohn Marino     }
36486d7f5d3SJohn Marino     /* Kill up to here */
36586d7f5d3SJohn Marino     p = strlen (mybuf + s) + 1;
36686d7f5d3SJohn Marino     expand_string (&buf, &buf_size, d + p);
36786d7f5d3SJohn Marino     memcpy (buf + d, mybuf + s, p);
36886d7f5d3SJohn Marino 
36986d7f5d3SJohn Marino     /* OK, buf contains the value we want to return.  Clean up and return
37086d7f5d3SJohn Marino        it.  */
37186d7f5d3SJohn Marino     free (mybuf);
37286d7f5d3SJohn Marino     /* Save a little memory with xstrdup; buf will tend to allocate
37386d7f5d3SJohn Marino        more than it needs to.  */
37486d7f5d3SJohn Marino     result = xstrdup (buf);
37586d7f5d3SJohn Marino     free (buf);
37686d7f5d3SJohn Marino     return result;
37786d7f5d3SJohn Marino 
37886d7f5d3SJohn Marino  error_exit:
37986d7f5d3SJohn Marino     if (mybuf) free (mybuf);
38086d7f5d3SJohn Marino     if (buf) free (buf);
38186d7f5d3SJohn Marino     return NULL;
38286d7f5d3SJohn Marino }
383