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