xref: /openbsd-src/gnu/usr.bin/cvs/src/expand_path.c (revision a4afd6dad3fba28f80e70208181c06c482259988)
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((char *env, char *file, int line));
19 
20 
21 /* User variables.  */
22 
23 List *variable_list = NULL;
24 
25 static void variable_delproc PROTO ((Node *));
26 
27 static void
28 variable_delproc (node)
29     Node *node;
30 {
31     free (node->data);
32 }
33 
34 /* Currently used by -s option; we might want a way to set user
35    variables in a file in the $CVSROOT/CVSROOT directory too.  */
36 
37 void
38 variable_set (nameval)
39     char *nameval;
40 {
41     char *p;
42     char *name;
43     Node *node;
44 
45     p = nameval;
46     while (isalnum (*p) || *p == '_')
47 	++p;
48     if (*p != '=')
49 	error (1, 0, "illegal character in user variable name in %s", nameval);
50     if (p == nameval)
51 	error (1, 0, "empty user variable name in %s", nameval);
52     name = xmalloc (p - nameval + 1);
53     strncpy (name, nameval, p - nameval);
54     name[p - nameval] = '\0';
55     /* Make p point to the value.  */
56     ++p;
57     if (strchr (p, '\012') != NULL)
58 	error (1, 0, "linefeed in user variable value in %s", nameval);
59 
60     if (variable_list == NULL)
61 	variable_list = getlist ();
62 
63     node = findnode (variable_list, name);
64     if (node == NULL)
65     {
66 	node = getnode ();
67 	node->type = VARIABLE;
68 	node->delproc = variable_delproc;
69 	node->key = name;
70 	node->data = xstrdup (p);
71 	(void) addnode (variable_list, node);
72     }
73     else
74     {
75 	/* Replace the old value.  For example, this means that -s
76 	   options on the command line override ones from .cvsrc.  */
77 	free (node->data);
78 	node->data = xstrdup (p);
79 	free (name);
80     }
81 }
82 
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
89    known.  */
90 char *
91 expand_path (name, file, line)
92     char *name;
93     char *file;
94     int line;
95 {
96     char *s;
97     char *d;
98     /* FIXME: arbitrary limit.  */
99     char  mybuf[PATH_MAX];
100     char  buf[PATH_MAX];
101     char *result;
102     s = name;
103     d = mybuf;
104     while ((*d++ = *s))
105 	if (*s++ == '$')
106 	{
107 	    char *p = d;
108 	    char *e;
109 	    int flag = (*s == '{');
110 
111 	    for (; (*d++ = *s); s++)
112 		if (flag
113 		    ? *s =='}'
114 		    : isalnum (*s) == 0 && *s != '_')
115 		    break;
116 	    *--d = 0;
117 	    e = expand_variable (&p[flag], file, line);
118 
119 	    if (e)
120 	    {
121 		for (d = &p[-1]; (*d++ = *e++);)
122 		    ;
123 		--d;
124 		if (flag && *s)
125 		    s++;
126 	    }
127 	    else
128 		/* expand_variable has already printed an error message.  */
129 		return NULL;
130 	}
131     *d = 0;
132     s = mybuf;
133     d = buf;
134     /* If you don't want ~username ~/ to be expanded simply remove
135      * This entire if statement including the else portion
136      */
137     if (*s++ == '~')
138     {
139 	char *t;
140 	char *p=s;
141 	if (*s=='/' || *s==0)
142 	    t = get_homedir ();
143 	else
144 	{
145 	    struct passwd *ps;
146 	    for (; *p!='/' && *p; p++)
147 		;
148 	    *p = 0;
149 	    ps = getpwnam (s);
150 	    if (ps == 0)
151 	    {
152 		if (line != 0)
153 		    error (0, 0, "%s:%d: no such user %s",
154 			   file, line, s);
155 		else
156 		    error (0, 0, "%s: no such user %s", file, s);
157 		return NULL;
158 	    }
159 	    t = ps->pw_dir;
160 	}
161 	while ((*d++ = *t++))
162 	    ;
163 	--d;
164 	if (*p == 0)
165 	    *p = '/';	       /* always add / */
166 	s=p;
167     }
168     else
169 	--s;
170 	/* Kill up to here */
171     while ((*d++ = *s++))
172 	;
173     *d=0;
174     result = xmalloc (sizeof(char) * strlen(buf)+1);
175     strcpy (result, buf);
176     return result;
177 }
178 
179 static char *
180 expand_variable (name, file, line)
181     char *name;
182     char *file;
183     int line;
184 {
185     if (strcmp (name, CVSROOT_ENV) == 0)
186 	return CVSroot_original;
187     else if (strcmp (name, RCSBIN_ENV)  == 0)
188 	return Rcsbin;
189     else if (strcmp (name, EDITOR1_ENV) == 0)
190 	return Editor;
191     else if (strcmp (name, EDITOR2_ENV) == 0)
192 	return Editor;
193     else if (strcmp (name, EDITOR3_ENV) == 0)
194 	return Editor;
195     else if (strcmp (name, "USER") == 0)
196 	return getcaller ();
197     else if (isalpha (name[0]))
198     {
199 	/* These names are reserved for future versions of CVS,
200 	   so that is why it is an error.  */
201 	if (line != 0)
202 	    error (0, 0, "%s:%d: no such internal variable $%s",
203 		   file, line, name);
204 	else
205 	    error (0, 0, "%s: no such internal variable $%s",
206 		   file, name);
207 	return NULL;
208     }
209     else if (name[0] == '=')
210     {
211 	Node *node;
212 	/* Crazy syntax for a user variable.  But we want
213 	   *something* that lets the user name a user variable
214 	   anything he wants, without interference from
215 	   (existing or future) internal variables.  */
216 	node = findnode (variable_list, name + 1);
217 	if (node == NULL)
218 	{
219 	    if (line != 0)
220 		error (0, 0, "%s:%d: no such user variable ${%s}",
221 		       file, line, name);
222 	    else
223 		error (0, 0, "%s: no such user variable ${%s}",
224 		       file, name);
225 	    return NULL;
226 	}
227 	return node->data;
228     }
229     else
230     {
231 	/* It is an unrecognized character.  We return an error to
232 	   reserve these for future versions of CVS; it is plausible
233 	   that various crazy syntaxes might be invented for inserting
234 	   information about revisions, branches, etc.  */
235 	if (line != 0)
236 	    error (0, 0, "%s:%d: unrecognized variable syntax %s",
237 		   file, line, name);
238 	else
239 	    error (0, 0, "%s: unrecognized variable syntax %s",
240 		   file, name);
241 	return NULL;
242     }
243 }
244