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 ((unsigned char) *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 99 char *mybuf = NULL; 100 size_t mybuf_size = 0; 101 char *buf = NULL; 102 size_t buf_size = 0; 103 104 size_t doff; 105 106 char *result; 107 108 /* Sorry this routine is so ugly; it is a head-on collision 109 between the `traditional' unix *d++ style and the need to 110 dynamically allocate. It would be much cleaner (and probably 111 faster, not that this is a bottleneck for CVS) with more use of 112 strcpy & friends, but I haven't taken the effort to rewrite it 113 thusly. */ 114 115 /* First copy from NAME to MYBUF, expanding $<foo> as we go. */ 116 s = name; 117 d = mybuf; 118 doff = d - mybuf; 119 expand_string (&mybuf, &mybuf_size, doff + 1); 120 d = mybuf + doff; 121 while ((*d++ = *s)) 122 { 123 if (*s++ == '$') 124 { 125 char *p = d; 126 char *e; 127 int flag = (*s == '{'); 128 129 doff = d - mybuf; 130 expand_string (&mybuf, &mybuf_size, doff + 1); 131 d = mybuf + doff; 132 for (; (*d++ = *s); s++) 133 { 134 if (flag 135 ? *s =='}' 136 : isalnum ((unsigned char) *s) == 0 && *s != '_') 137 break; 138 doff = d - mybuf; 139 expand_string (&mybuf, &mybuf_size, doff + 1); 140 d = mybuf + doff; 141 } 142 *--d = '\0'; 143 e = expand_variable (&p[flag], file, line); 144 145 if (e) 146 { 147 doff = d - mybuf; 148 expand_string (&mybuf, &mybuf_size, doff + 1); 149 d = mybuf + doff; 150 for (d = &p[-1]; (*d++ = *e++);) 151 { 152 doff = d - mybuf; 153 expand_string (&mybuf, &mybuf_size, doff + 1); 154 d = mybuf + doff; 155 } 156 --d; 157 if (flag && *s) 158 s++; 159 } 160 else 161 /* expand_variable has already printed an error message. */ 162 goto error_exit; 163 } 164 doff = d - mybuf; 165 expand_string (&mybuf, &mybuf_size, doff + 1); 166 d = mybuf + doff; 167 } 168 doff = d - mybuf; 169 expand_string (&mybuf, &mybuf_size, doff + 1); 170 d = mybuf + doff; 171 *d = '\0'; 172 173 /* Then copy from MYBUF to BUF, expanding ~. */ 174 s = mybuf; 175 d = buf; 176 /* If you don't want ~username ~/ to be expanded simply remove 177 * This entire if statement including the else portion 178 */ 179 if (*s++ == '~') 180 { 181 char *t; 182 char *p=s; 183 if (*s=='/' || *s==0) 184 t = get_homedir (); 185 else 186 { 187 #ifdef GETPWNAM_MISSING 188 for (; *p!='/' && *p; p++) 189 ; 190 *p = 0; 191 if (line != 0) 192 error (0, 0, 193 "%s:%d:tilde expansion not supported on this system", 194 file, line); 195 else 196 error (0, 0, "%s:tilde expansion not supported on this system", 197 file); 198 return NULL; 199 #else 200 struct passwd *ps; 201 for (; *p!='/' && *p; p++) 202 ; 203 *p = 0; 204 ps = getpwnam (s); 205 if (ps == 0) 206 { 207 if (line != 0) 208 error (0, 0, "%s:%d: no such user %s", 209 file, line, s); 210 else 211 error (0, 0, "%s: no such user %s", file, s); 212 return NULL; 213 } 214 t = ps->pw_dir; 215 #endif 216 } 217 if (t == NULL) 218 error (1, 0, "cannot find home directory"); 219 220 doff = d - buf; 221 expand_string (&buf, &buf_size, doff + 1); 222 d = buf + doff; 223 while ((*d++ = *t++)) 224 { 225 doff = d - buf; 226 expand_string (&buf, &buf_size, doff + 1); 227 d = buf + doff; 228 } 229 --d; 230 if (*p == 0) 231 *p = '/'; /* always add / */ 232 s=p; 233 } 234 else 235 --s; 236 /* Kill up to here */ 237 doff = d - buf; 238 expand_string (&buf, &buf_size, doff + 1); 239 d = buf + doff; 240 while ((*d++ = *s++)) 241 { 242 doff = d - buf; 243 expand_string (&buf, &buf_size, doff + 1); 244 d = buf + doff; 245 } 246 doff = d - buf; 247 expand_string (&buf, &buf_size, doff + 1); 248 d = buf + doff; 249 *d = '\0'; 250 251 /* OK, buf contains the value we want to return. Clean up and return 252 it. */ 253 free (mybuf); 254 /* Save a little memory with xstrdup; buf will tend to allocate 255 more than it needs to. */ 256 result = xstrdup (buf); 257 free (buf); 258 return result; 259 260 error_exit: 261 if (mybuf != NULL) 262 free (mybuf); 263 if (buf != NULL) 264 free (buf); 265 return NULL; 266 } 267 268 static char * 269 expand_variable (name, file, line) 270 char *name; 271 char *file; 272 int line; 273 { 274 if (strcmp (name, CVSROOT_ENV) == 0) 275 return current_parsed_root->original; 276 else if (strcmp (name, "RCSBIN") == 0) 277 { 278 error (0, 0, "RCSBIN internal variable is no longer supported"); 279 return NULL; 280 } 281 else if (strcmp (name, EDITOR1_ENV) == 0) 282 return Editor; 283 else if (strcmp (name, EDITOR2_ENV) == 0) 284 return Editor; 285 else if (strcmp (name, EDITOR3_ENV) == 0) 286 return Editor; 287 else if (strcmp (name, "USER") == 0) 288 return getcaller (); 289 else if (strcmp (name, "SESSIONID") == 0 || strcmp (name, "COMMITID") == 0) 290 return global_session_id; 291 else if (isalpha ((unsigned char) name[0])) 292 { 293 /* These names are reserved for future versions of CVS, 294 so that is why it is an error. */ 295 if (line != 0) 296 error (0, 0, "%s:%d: no such internal variable $%s", 297 file, line, name); 298 else 299 error (0, 0, "%s: no such internal variable $%s", 300 file, name); 301 return NULL; 302 } 303 else if (name[0] == '=') 304 { 305 Node *node; 306 /* Crazy syntax for a user variable. But we want 307 *something* that lets the user name a user variable 308 anything he wants, without interference from 309 (existing or future) internal variables. */ 310 node = findnode (variable_list, name + 1); 311 if (node == NULL) 312 { 313 if (line != 0) 314 error (0, 0, "%s:%d: no such user variable ${%s}", 315 file, line, name); 316 else 317 error (0, 0, "%s: no such user variable ${%s}", 318 file, name); 319 return NULL; 320 } 321 return node->data; 322 } 323 else 324 { 325 /* It is an unrecognized character. We return an error to 326 reserve these for future versions of CVS; it is plausible 327 that various crazy syntaxes might be invented for inserting 328 information about revisions, branches, etc. */ 329 if (line != 0) 330 error (0, 0, "%s:%d: unrecognized variable syntax %s", 331 file, line, name); 332 else 333 error (0, 0, "%s: unrecognized variable syntax %s", 334 file, name); 335 return NULL; 336 } 337 } 338