| File: | src/gnu/usr.bin/cvs/src/expand_path.c |
| Warning: | line 149, column 3 Value stored to 'd' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 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))(char *env, char *file, int line); |
| 19 | |
| 20 | |
| 21 | /* User variables. */ |
| 22 | |
| 23 | List *variable_list = NULL((void*)0); |
| 24 | |
| 25 | static void variable_delproc PROTO ((Node *))(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((void*)0)) |
| 58 | error (1, 0, "linefeed in user variable value in %s", nameval); |
| 59 | |
| 60 | if (variable_list == NULL((void*)0)) |
| 61 | variable_list = getlist (); |
| 62 | |
| 63 | node = findnode (variable_list, name); |
| 64 | if (node == NULL((void*)0)) |
| 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((void*)0); |
| 100 | size_t mybuf_size = 0; |
| 101 | char *buf = NULL((void*)0); |
| 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; |
Value stored to 'd' is never read | |
| 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((void*)0); |
| 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((void*)0); |
| 213 | } |
| 214 | t = ps->pw_dir; |
| 215 | #endif |
| 216 | } |
| 217 | if (t == NULL((void*)0)) |
| 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((void*)0)) |
| 262 | free (mybuf); |
| 263 | if (buf != NULL((void*)0)) |
| 264 | free (buf); |
| 265 | return NULL((void*)0); |
| 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"CVSROOT") == 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((void*)0); |
| 280 | } |
| 281 | else if (strcmp (name, EDITOR1_ENV"CVSEDITOR") == 0) |
| 282 | return Editor; |
| 283 | else if (strcmp (name, EDITOR2_ENV"VISUAL") == 0) |
| 284 | return Editor; |
| 285 | else if (strcmp (name, EDITOR3_ENV"EDITOR") == 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((void*)0); |
| 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((void*)0)) |
| 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((void*)0); |
| 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((void*)0); |
| 336 | } |
| 337 | } |