[29] | 1 | /* |
---|
| 2 | * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. |
---|
| 3 | * |
---|
| 4 | * This file is part of Jam - see jam.c for Copyright information. |
---|
| 5 | */ |
---|
| 6 | |
---|
| 7 | # include "jam.h" |
---|
| 8 | # include "lists.h" |
---|
| 9 | # include "variable.h" |
---|
| 10 | # include "expand.h" |
---|
| 11 | # include "pathsys.h" |
---|
| 12 | # include "newstr.h" |
---|
| 13 | # include <assert.h> |
---|
| 14 | |
---|
| 15 | # ifdef OS_CYGWIN |
---|
| 16 | # include <sys/cygwin.h> |
---|
| 17 | # include <windows.h> |
---|
| 18 | # include <stdlib.h> |
---|
| 19 | # endif |
---|
| 20 | |
---|
| 21 | /* |
---|
| 22 | * expand.c - expand a buffer, given variable values |
---|
| 23 | * |
---|
| 24 | * External routines: |
---|
| 25 | * |
---|
| 26 | * var_expand() - variable-expand input string into list of strings |
---|
| 27 | * |
---|
| 28 | * Internal routines: |
---|
| 29 | * |
---|
| 30 | * var_edit_parse() - parse : modifiers into PATHNAME structure |
---|
| 31 | * var_edit_file() - copy input target name to output, modifying filename |
---|
| 32 | * var_edit_shift() - do upshift/downshift mods |
---|
| 33 | * |
---|
| 34 | * 01/25/94 (seiwald) - $(X)$(UNDEF) was expanding like plain $(X) |
---|
| 35 | * 04/13/94 (seiwald) - added shorthand L0 for null list pointer |
---|
| 36 | * 01/11/01 (seiwald) - added support for :E=emptyvalue, :J=joinval |
---|
| 37 | */ |
---|
| 38 | |
---|
| 39 | typedef struct { |
---|
| 40 | PATHNAME f; /* :GDBSMR -- pieces */ |
---|
| 41 | char parent; /* :P -- go to parent directory */ |
---|
| 42 | char filemods; /* one of the above applied */ |
---|
| 43 | char downshift; /* :L -- downshift result */ |
---|
| 44 | char upshift; /* :U -- upshift result */ |
---|
| 45 | char to_slashes; /* :T -- convert "\" to "/" */ |
---|
| 46 | char to_windows; /* :W -- convert cygwin to native paths */ |
---|
| 47 | PATHPART empty; /* :E -- default for empties */ |
---|
| 48 | PATHPART join; /* :J -- join list with char */ |
---|
| 49 | } VAR_EDITS ; |
---|
| 50 | |
---|
| 51 | static void var_edit_parse( char *mods, VAR_EDITS *edits ); |
---|
| 52 | static void var_edit_file( char *in, string *out, VAR_EDITS *edits ); |
---|
| 53 | static void var_edit_shift( string *out, VAR_EDITS *edits ); |
---|
| 54 | |
---|
| 55 | # define MAGIC_COLON '\001' |
---|
| 56 | # define MAGIC_LEFT '\002' |
---|
| 57 | # define MAGIC_RIGHT '\003' |
---|
| 58 | |
---|
| 59 | /* |
---|
| 60 | * var_expand() - variable-expand input string into list of strings |
---|
| 61 | * |
---|
| 62 | * Would just copy input to output, performing variable expansion, |
---|
| 63 | * except that since variables can contain multiple values the result |
---|
| 64 | * of variable expansion may contain multiple values (a list). Properly |
---|
| 65 | * performs "product" operations that occur in "$(var1)xxx$(var2)" or |
---|
| 66 | * even "$($(var2))". |
---|
| 67 | * |
---|
| 68 | * Returns a newly created list. |
---|
| 69 | */ |
---|
| 70 | |
---|
| 71 | LIST * |
---|
| 72 | var_expand( |
---|
| 73 | LIST *l, |
---|
| 74 | char *in, |
---|
| 75 | char *end, |
---|
| 76 | LOL *lol, |
---|
| 77 | int cancopyin ) |
---|
| 78 | { |
---|
| 79 | char out_buf[ MAXSYM ]; |
---|
| 80 | string buf[1]; |
---|
| 81 | string out1[1]; /* Temporary buffer */ |
---|
| 82 | size_t prefix_length; |
---|
| 83 | char *out; |
---|
| 84 | char *inp = in; |
---|
| 85 | char *ov; /* for temp copy of variable in outbuf */ |
---|
| 86 | int depth; |
---|
| 87 | |
---|
| 88 | if( DEBUG_VAREXP ) |
---|
| 89 | printf( "expand '%.*s'\n", end - in, in ); |
---|
| 90 | |
---|
| 91 | /* This gets alot of cases: $(<) and $(>) */ |
---|
| 92 | |
---|
| 93 | if( in[0] == '$' && in[1] == '(' && in[3] == ')' && !in[4] ) |
---|
| 94 | { |
---|
| 95 | switch( in[2] ) |
---|
| 96 | { |
---|
| 97 | case '1': |
---|
| 98 | case '<': |
---|
| 99 | return list_copy( l, lol_get( lol, 0 ) ); |
---|
| 100 | |
---|
| 101 | case '2': |
---|
| 102 | case '>': |
---|
| 103 | return list_copy( l, lol_get( lol, 1 ) ); |
---|
| 104 | } |
---|
| 105 | } |
---|
| 106 | |
---|
| 107 | /* Just try simple copy of in to out. */ |
---|
| 108 | |
---|
| 109 | while( in < end ) |
---|
| 110 | if( *in++ == '$' && *in == '(' ) |
---|
| 111 | goto expand; |
---|
| 112 | |
---|
| 113 | /* No variables expanded - just add copy of input string to list. */ |
---|
| 114 | |
---|
| 115 | /* Cancopyin is an optimization: if the input was already a list */ |
---|
| 116 | /* item, we can use the copystr() to put it on the new list. */ |
---|
| 117 | /* Otherwise, we use the slower newstr(). */ |
---|
| 118 | |
---|
| 119 | if( cancopyin ) |
---|
| 120 | { |
---|
| 121 | return list_new( l, copystr( inp ) ); |
---|
| 122 | } |
---|
| 123 | else |
---|
| 124 | { |
---|
| 125 | LIST* r; |
---|
| 126 | string_new( buf ); |
---|
| 127 | string_append_range( buf, inp, end ); |
---|
| 128 | |
---|
| 129 | r = list_new( l, newstr( buf->value) ); |
---|
| 130 | string_free( buf ); |
---|
| 131 | return r; |
---|
| 132 | } |
---|
| 133 | |
---|
| 134 | expand: |
---|
| 135 | string_new( buf ); |
---|
| 136 | string_append_range( buf, inp, in - 1); /* copy the part before '$'. */ |
---|
| 137 | /* |
---|
| 138 | * Input so far (ignore blanks): |
---|
| 139 | * |
---|
| 140 | * stuff-in-outbuf $(variable) remainder |
---|
| 141 | * ^ ^ |
---|
| 142 | * in end |
---|
| 143 | * Output so far: |
---|
| 144 | * |
---|
| 145 | * stuff-in-outbuf $ |
---|
| 146 | * ^ ^ |
---|
| 147 | * out_buf out |
---|
| 148 | * |
---|
| 149 | * |
---|
| 150 | * We just copied the $ of $(...), so back up one on the output. |
---|
| 151 | * We now find the matching close paren, copying the variable and |
---|
| 152 | * modifiers between the $( and ) temporarily into out_buf, so that |
---|
| 153 | * we can replace :'s with MAGIC_COLON. This is necessary to avoid |
---|
| 154 | * being confused by modifier values that are variables containing |
---|
| 155 | * :'s. Ugly. |
---|
| 156 | */ |
---|
| 157 | |
---|
| 158 | depth = 1; |
---|
| 159 | inp = ++in; /* skip over the '(' */ |
---|
| 160 | |
---|
| 161 | while( in < end && depth ) |
---|
| 162 | { |
---|
| 163 | switch( *in++ ) |
---|
| 164 | { |
---|
| 165 | case '(': depth++; break; |
---|
| 166 | case ')': depth--; break; |
---|
| 167 | } |
---|
| 168 | } |
---|
| 169 | |
---|
| 170 | /* |
---|
| 171 | * Input so far (ignore blanks): |
---|
| 172 | * |
---|
| 173 | * stuff-in-outbuf $(variable) remainder |
---|
| 174 | * ^ ^ ^ |
---|
| 175 | * inp in end |
---|
| 176 | */ |
---|
| 177 | prefix_length = buf->size; |
---|
| 178 | string_append_range( buf, inp, in - 1 ); |
---|
| 179 | |
---|
| 180 | out = buf->value + prefix_length; |
---|
| 181 | for ( ov = out; ov < buf->value + buf->size; ++ov ) |
---|
| 182 | { |
---|
| 183 | switch( *ov ) |
---|
| 184 | { |
---|
| 185 | case ':': *ov = MAGIC_COLON; break; |
---|
| 186 | case '[': *ov = MAGIC_LEFT; break; |
---|
| 187 | case ']': *ov = MAGIC_RIGHT; break; |
---|
| 188 | } |
---|
| 189 | } |
---|
| 190 | |
---|
| 191 | /* |
---|
| 192 | * Input so far (ignore blanks): |
---|
| 193 | * |
---|
| 194 | * stuff-in-outbuf $(variable) remainder |
---|
| 195 | * ^ ^ |
---|
| 196 | * in end |
---|
| 197 | * Output so far: |
---|
| 198 | * |
---|
| 199 | * stuff-in-outbuf variable |
---|
| 200 | * ^ ^ ^ |
---|
| 201 | * out_buf out ov |
---|
| 202 | * |
---|
| 203 | * Later we will overwrite 'variable' in out_buf, but we'll be |
---|
| 204 | * done with it by then. 'variable' may be a multi-element list, |
---|
| 205 | * so may each value for '$(variable element)', and so may 'remainder'. |
---|
| 206 | * Thus we produce a product of three lists. |
---|
| 207 | */ |
---|
| 208 | |
---|
| 209 | { |
---|
| 210 | LIST *variables = 0; |
---|
| 211 | LIST *remainder = 0; |
---|
| 212 | LIST *vars; |
---|
| 213 | |
---|
| 214 | /* Recursively expand variable name & rest of input */ |
---|
| 215 | |
---|
| 216 | if( out < ov ) |
---|
| 217 | variables = var_expand( L0, out, ov, lol, 0 ); |
---|
| 218 | if( in < end ) |
---|
| 219 | remainder = var_expand( L0, in, end, lol, 0 ); |
---|
| 220 | |
---|
| 221 | /* Now produce the result chain */ |
---|
| 222 | |
---|
| 223 | /* For each variable name */ |
---|
| 224 | |
---|
| 225 | for( vars = variables; vars; vars = list_next( vars ) ) |
---|
| 226 | { |
---|
| 227 | LIST *value, *evalue = 0; |
---|
| 228 | char *colon; |
---|
| 229 | char *bracket; |
---|
| 230 | string variable[1]; |
---|
| 231 | char *varname; |
---|
| 232 | int sub1 = 0, sub2 = -1; |
---|
| 233 | VAR_EDITS edits; |
---|
| 234 | |
---|
| 235 | /* Look for a : modifier in the variable name */ |
---|
| 236 | /* Must copy into varname so we can modify it */ |
---|
| 237 | |
---|
| 238 | string_copy( variable, vars->string ); |
---|
| 239 | varname = variable->value; |
---|
| 240 | |
---|
| 241 | if( colon = strchr( varname, MAGIC_COLON ) ) |
---|
| 242 | { |
---|
| 243 | string_truncate( variable, colon - varname ); |
---|
| 244 | var_edit_parse( colon + 1, &edits ); |
---|
| 245 | } |
---|
| 246 | |
---|
| 247 | /* Look for [x-y] subscripting */ |
---|
| 248 | /* sub1 and sub2 are x and y. */ |
---|
| 249 | |
---|
| 250 | if ( bracket = strchr( varname, MAGIC_LEFT ) ) |
---|
| 251 | { |
---|
| 252 | /* |
---|
| 253 | ** Make all syntax errors in [] subscripting |
---|
| 254 | ** result in the same behavior: silenty return an empty |
---|
| 255 | ** expansion (by setting sub2 = 0). Brute force parsing; |
---|
| 256 | ** May get moved into yacc someday. |
---|
| 257 | */ |
---|
| 258 | |
---|
| 259 | char *s = bracket + 1; |
---|
| 260 | |
---|
| 261 | string_truncate( variable, bracket - varname ); |
---|
| 262 | |
---|
| 263 | do /* so we can use "break" */ |
---|
| 264 | { |
---|
| 265 | /* Allow negative indexes. */ |
---|
| 266 | if (! isdigit( *s ) && ! ( *s == '-') ) |
---|
| 267 | { |
---|
| 268 | sub2 = 0; |
---|
| 269 | break; |
---|
| 270 | } |
---|
| 271 | sub1 = atoi(s); |
---|
| 272 | |
---|
| 273 | /* Skip over the first symbol, which is either a digit or dash. */ |
---|
| 274 | s++; |
---|
| 275 | while ( isdigit( *s ) ) s++; |
---|
| 276 | |
---|
| 277 | if ( *s == MAGIC_RIGHT ) |
---|
| 278 | { |
---|
| 279 | sub2 = sub1; |
---|
| 280 | break; |
---|
| 281 | } |
---|
| 282 | |
---|
| 283 | if ( *s != '-') |
---|
| 284 | { |
---|
| 285 | sub2 = 0; |
---|
| 286 | break; |
---|
| 287 | } |
---|
| 288 | |
---|
| 289 | s++; |
---|
| 290 | |
---|
| 291 | if ( *s == MAGIC_RIGHT ) |
---|
| 292 | { |
---|
| 293 | sub2 = -1; |
---|
| 294 | break; |
---|
| 295 | } |
---|
| 296 | |
---|
| 297 | if (! isdigit( *s ) && ! ( *s == '-') ) |
---|
| 298 | { |
---|
| 299 | sub2 = 0; |
---|
| 300 | break; |
---|
| 301 | } |
---|
| 302 | |
---|
| 303 | /* First, compute the index of the last element. */ |
---|
| 304 | sub2 = atoi(s); |
---|
| 305 | s++; |
---|
| 306 | while ( isdigit( *s ) ) s++; |
---|
| 307 | |
---|
| 308 | if ( *s != MAGIC_RIGHT) |
---|
| 309 | sub2 = 0; |
---|
| 310 | |
---|
| 311 | } while (0); |
---|
| 312 | |
---|
| 313 | /* |
---|
| 314 | ** Anything but the end of the string, or the colon |
---|
| 315 | ** introducing a modifier is a syntax error. |
---|
| 316 | */ |
---|
| 317 | |
---|
| 318 | s++; |
---|
| 319 | if (*s && *s != MAGIC_COLON) |
---|
| 320 | sub2 = 0; |
---|
| 321 | |
---|
| 322 | *bracket = '\0'; |
---|
| 323 | } |
---|
| 324 | |
---|
| 325 | /* Get variable value, specially handling $(<), $(>), $(n) */ |
---|
| 326 | |
---|
| 327 | if( varname[0] == '<' && !varname[1] ) |
---|
| 328 | value = lol_get( lol, 0 ); |
---|
| 329 | else if( varname[0] == '>' && !varname[1] ) |
---|
| 330 | value = lol_get( lol, 1 ); |
---|
| 331 | else if( varname[0] >= '1' && varname[0] <= '9' && !varname[1] ) |
---|
| 332 | value = lol_get( lol, varname[0] - '1' ); |
---|
| 333 | else |
---|
| 334 | value = var_get( varname ); |
---|
| 335 | |
---|
| 336 | /* Handle negitive indexes: part two. */ |
---|
| 337 | { |
---|
| 338 | int length = list_length( value ); |
---|
| 339 | |
---|
| 340 | if (sub1 < 0) |
---|
| 341 | sub1 = length + sub1; |
---|
| 342 | else |
---|
| 343 | sub1 -= 1; |
---|
| 344 | |
---|
| 345 | if (sub2 < 0) |
---|
| 346 | sub2 = length + 1 + sub2 - sub1; |
---|
| 347 | else |
---|
| 348 | sub2 -= sub1; |
---|
| 349 | /* |
---|
| 350 | ** The "sub2 < 0" test handles the semantic error |
---|
| 351 | ** of sub2 < sub1. |
---|
| 352 | */ |
---|
| 353 | if ( sub2 < 0 ) |
---|
| 354 | sub2 = 0; |
---|
| 355 | } |
---|
| 356 | |
---|
| 357 | |
---|
| 358 | |
---|
| 359 | /* The fast path: $(x) - just copy the variable value. */ |
---|
| 360 | /* This is only an optimization */ |
---|
| 361 | |
---|
| 362 | if( out == out_buf && !bracket && !colon && in == end ) |
---|
| 363 | { |
---|
| 364 | string_free( variable ); |
---|
| 365 | l = list_copy( l, value ); |
---|
| 366 | continue; |
---|
| 367 | } |
---|
| 368 | |
---|
| 369 | /* Handle start subscript */ |
---|
| 370 | |
---|
| 371 | while( sub1 > 0 && value ) |
---|
| 372 | --sub1, value = list_next( value ); |
---|
| 373 | |
---|
| 374 | /* Empty w/ :E=default? */ |
---|
| 375 | |
---|
| 376 | if( !value && colon && edits.empty.ptr ) |
---|
| 377 | evalue = value = list_new( L0, newstr( edits.empty.ptr ) ); |
---|
| 378 | |
---|
| 379 | /* For each variable value */ |
---|
| 380 | |
---|
| 381 | string_new( out1 ); |
---|
| 382 | for( ; value; value = list_next( value ) ) |
---|
| 383 | { |
---|
| 384 | LIST *rem; |
---|
| 385 | size_t postfix_start; |
---|
| 386 | |
---|
| 387 | /* Handle end subscript (length actually) */ |
---|
| 388 | |
---|
| 389 | if( sub2 >= 0 && --sub2 < 0 ) |
---|
| 390 | break; |
---|
| 391 | |
---|
| 392 | string_truncate( buf, prefix_length ); |
---|
| 393 | |
---|
| 394 | /* Apply : mods, if present */ |
---|
| 395 | |
---|
| 396 | if( colon && edits.filemods ) |
---|
| 397 | var_edit_file( value->string, out1, &edits ); |
---|
| 398 | else |
---|
| 399 | string_append( out1, value->string ); |
---|
| 400 | |
---|
| 401 | if( colon && ( edits.upshift || edits.downshift || edits.to_slashes || edits.to_windows ) ) |
---|
| 402 | var_edit_shift( out1, &edits ); |
---|
| 403 | |
---|
| 404 | /* Handle :J=joinval */ |
---|
| 405 | /* If we have more values for this var, just */ |
---|
| 406 | /* keep appending them (with the join value) */ |
---|
| 407 | /* rather than creating separate LIST elements. */ |
---|
| 408 | |
---|
| 409 | if( colon && edits.join.ptr && |
---|
| 410 | ( list_next( value ) || list_next( vars ) ) ) |
---|
| 411 | { |
---|
| 412 | string_append( out1, edits.join.ptr ); |
---|
| 413 | continue; |
---|
| 414 | } |
---|
| 415 | |
---|
| 416 | string_append( buf, out1->value ); |
---|
| 417 | string_free( out1 ); |
---|
| 418 | string_new( out1 ); |
---|
| 419 | |
---|
| 420 | /* If no remainder, append result to output chain. */ |
---|
| 421 | |
---|
| 422 | if( in == end ) |
---|
| 423 | { |
---|
| 424 | l = list_new( l, newstr( buf->value ) ); |
---|
| 425 | continue; |
---|
| 426 | } |
---|
| 427 | |
---|
| 428 | /* For each remainder, append the complete string */ |
---|
| 429 | /* to the output chain. */ |
---|
| 430 | /* Remember the end of the variable expansion so */ |
---|
| 431 | /* we can just tack on each instance of 'remainder' */ |
---|
| 432 | |
---|
| 433 | postfix_start = buf->size; |
---|
| 434 | |
---|
| 435 | for( rem = remainder; rem; rem = list_next( rem ) ) |
---|
| 436 | { |
---|
| 437 | string_truncate( buf, postfix_start ); |
---|
| 438 | string_append( buf, rem->string ); |
---|
| 439 | l = list_new( l, newstr( buf->value ) ); |
---|
| 440 | } |
---|
| 441 | } |
---|
| 442 | string_free( out1 ); |
---|
| 443 | |
---|
| 444 | /* Toss used empty */ |
---|
| 445 | |
---|
| 446 | if( evalue ) |
---|
| 447 | list_free( evalue ); |
---|
| 448 | |
---|
| 449 | string_free( variable ); |
---|
| 450 | } |
---|
| 451 | |
---|
| 452 | /* variables & remainder were gifts from var_expand */ |
---|
| 453 | /* and must be freed */ |
---|
| 454 | |
---|
| 455 | if( variables ) |
---|
| 456 | list_free( variables ); |
---|
| 457 | if( remainder) |
---|
| 458 | list_free( remainder ); |
---|
| 459 | |
---|
| 460 | if( DEBUG_VAREXP ) |
---|
| 461 | { |
---|
| 462 | printf( "expanded to " ); |
---|
| 463 | list_print( l ); |
---|
| 464 | printf( "\n" ); |
---|
| 465 | } |
---|
| 466 | |
---|
| 467 | string_free( buf ); |
---|
| 468 | return l; |
---|
| 469 | } |
---|
| 470 | } |
---|
| 471 | |
---|
| 472 | /* |
---|
| 473 | * var_edit_parse() - parse : modifiers into PATHNAME structure |
---|
| 474 | * |
---|
| 475 | * The : modifiers in a $(varname:modifier) currently support replacing |
---|
| 476 | * or omitting elements of a filename, and so they are parsed into a |
---|
| 477 | * PATHNAME structure (which contains pointers into the original string). |
---|
| 478 | * |
---|
| 479 | * Modifiers of the form "X=value" replace the component X with |
---|
| 480 | * the given value. Modifiers without the "=value" cause everything |
---|
| 481 | * but the component X to be omitted. X is one of: |
---|
| 482 | * |
---|
| 483 | * G <grist> |
---|
| 484 | * D directory name |
---|
| 485 | * B base name |
---|
| 486 | * S .suffix |
---|
| 487 | * M (member) |
---|
| 488 | * R root directory - prepended to whole path |
---|
| 489 | * |
---|
| 490 | * This routine sets: |
---|
| 491 | * |
---|
| 492 | * f->f_xxx.ptr = 0 |
---|
| 493 | * f->f_xxx.len = 0 |
---|
| 494 | * -> leave the original component xxx |
---|
| 495 | * |
---|
| 496 | * f->f_xxx.ptr = string |
---|
| 497 | * f->f_xxx.len = strlen( string ) |
---|
| 498 | * -> replace component xxx with string |
---|
| 499 | * |
---|
| 500 | * f->f_xxx.ptr = "" |
---|
| 501 | * f->f_xxx.len = 0 |
---|
| 502 | * -> omit component xxx |
---|
| 503 | * |
---|
| 504 | * var_edit_file() below and path_build() obligingly follow this convention. |
---|
| 505 | */ |
---|
| 506 | |
---|
| 507 | static void |
---|
| 508 | var_edit_parse( |
---|
| 509 | char *mods, |
---|
| 510 | VAR_EDITS *edits ) |
---|
| 511 | { |
---|
| 512 | int havezeroed = 0; |
---|
| 513 | memset( (char *)edits, 0, sizeof( *edits ) ); |
---|
| 514 | |
---|
| 515 | while( *mods ) |
---|
| 516 | { |
---|
| 517 | char *p; |
---|
| 518 | PATHPART *fp; |
---|
| 519 | |
---|
| 520 | switch( *mods++ ) |
---|
| 521 | { |
---|
| 522 | case 'L': edits->downshift = 1; continue; |
---|
| 523 | case 'U': edits->upshift = 1; continue; |
---|
| 524 | case 'P': edits->parent = edits->filemods = 1; continue; |
---|
| 525 | case 'E': fp = &edits->empty; goto strval; |
---|
| 526 | case 'J': fp = &edits->join; goto strval; |
---|
| 527 | case 'G': fp = &edits->f.f_grist; goto fileval; |
---|
| 528 | case 'R': fp = &edits->f.f_root; goto fileval; |
---|
| 529 | case 'D': fp = &edits->f.f_dir; goto fileval; |
---|
| 530 | case 'B': fp = &edits->f.f_base; goto fileval; |
---|
| 531 | case 'S': fp = &edits->f.f_suffix; goto fileval; |
---|
| 532 | case 'M': fp = &edits->f.f_member; goto fileval; |
---|
| 533 | case 'T': edits->to_slashes = 1; continue; |
---|
| 534 | case 'W': edits->to_windows = 1; continue; |
---|
| 535 | |
---|
| 536 | default: return; /* should complain, but so what... */ |
---|
| 537 | } |
---|
| 538 | |
---|
| 539 | fileval: |
---|
| 540 | |
---|
| 541 | /* Handle :CHARS, where each char (without a following =) */ |
---|
| 542 | /* selects a particular file path element. On the first such */ |
---|
| 543 | /* char, we deselect all others (by setting ptr = "", len = 0) */ |
---|
| 544 | /* and for each char we select that element (by setting ptr = 0) */ |
---|
| 545 | |
---|
| 546 | edits->filemods = 1; |
---|
| 547 | |
---|
| 548 | if( *mods != '=' ) |
---|
| 549 | { |
---|
| 550 | int i; |
---|
| 551 | |
---|
| 552 | if( !havezeroed++ ) |
---|
| 553 | for( i = 0; i < 6; i++ ) |
---|
| 554 | { |
---|
| 555 | edits->f.part[ i ].len = 0; |
---|
| 556 | edits->f.part[ i ].ptr = ""; |
---|
| 557 | } |
---|
| 558 | |
---|
| 559 | fp->ptr = 0; |
---|
| 560 | continue; |
---|
| 561 | } |
---|
| 562 | |
---|
| 563 | strval: |
---|
| 564 | |
---|
| 565 | /* Handle :X=value, or :X */ |
---|
| 566 | |
---|
| 567 | if( *mods != '=' ) |
---|
| 568 | { |
---|
| 569 | fp->ptr = ""; |
---|
| 570 | fp->len = 0; |
---|
| 571 | } |
---|
| 572 | else if( p = strchr( mods, MAGIC_COLON ) ) |
---|
| 573 | { |
---|
| 574 | *p = 0; |
---|
| 575 | fp->ptr = ++mods; |
---|
| 576 | fp->len = p - mods; |
---|
| 577 | mods = p + 1; |
---|
| 578 | } |
---|
| 579 | else |
---|
| 580 | { |
---|
| 581 | fp->ptr = ++mods; |
---|
| 582 | fp->len = strlen( mods ); |
---|
| 583 | mods += fp->len; |
---|
| 584 | } |
---|
| 585 | } |
---|
| 586 | } |
---|
| 587 | |
---|
| 588 | /* |
---|
| 589 | * var_edit_file() - copy input target name to output, modifying filename |
---|
| 590 | */ |
---|
| 591 | |
---|
| 592 | static void |
---|
| 593 | var_edit_file( |
---|
| 594 | char *in, |
---|
| 595 | string *out, |
---|
| 596 | VAR_EDITS *edits ) |
---|
| 597 | { |
---|
| 598 | PATHNAME pathname; |
---|
| 599 | |
---|
| 600 | /* Parse apart original filename, putting parts into "pathname" */ |
---|
| 601 | |
---|
| 602 | path_parse( in, &pathname ); |
---|
| 603 | |
---|
| 604 | /* Replace any pathname with edits->f */ |
---|
| 605 | |
---|
| 606 | if( edits->f.f_grist.ptr ) |
---|
| 607 | pathname.f_grist = edits->f.f_grist; |
---|
| 608 | |
---|
| 609 | if( edits->f.f_root.ptr ) |
---|
| 610 | pathname.f_root = edits->f.f_root; |
---|
| 611 | |
---|
| 612 | if( edits->f.f_dir.ptr ) |
---|
| 613 | pathname.f_dir = edits->f.f_dir; |
---|
| 614 | |
---|
| 615 | if( edits->f.f_base.ptr ) |
---|
| 616 | pathname.f_base = edits->f.f_base; |
---|
| 617 | |
---|
| 618 | if( edits->f.f_suffix.ptr ) |
---|
| 619 | pathname.f_suffix = edits->f.f_suffix; |
---|
| 620 | |
---|
| 621 | if( edits->f.f_member.ptr ) |
---|
| 622 | pathname.f_member = edits->f.f_member; |
---|
| 623 | |
---|
| 624 | /* If requested, modify pathname to point to parent */ |
---|
| 625 | |
---|
| 626 | if( edits->parent ) |
---|
| 627 | path_parent( &pathname ); |
---|
| 628 | |
---|
| 629 | /* Put filename back together */ |
---|
| 630 | |
---|
| 631 | path_build( &pathname, out, 0 ); |
---|
| 632 | } |
---|
| 633 | |
---|
| 634 | /* |
---|
| 635 | * var_edit_shift() - do upshift/downshift mods |
---|
| 636 | */ |
---|
| 637 | |
---|
| 638 | static void |
---|
| 639 | var_edit_shift( |
---|
| 640 | string *out, |
---|
| 641 | VAR_EDITS *edits ) |
---|
| 642 | { |
---|
| 643 | /* Handle upshifting, downshifting and slash translation now */ |
---|
| 644 | |
---|
| 645 | char *p; |
---|
| 646 | for ( p = out->value; *p; ++p) |
---|
| 647 | { |
---|
| 648 | if (edits->upshift) |
---|
| 649 | { |
---|
| 650 | *p = toupper( *p ); |
---|
| 651 | } |
---|
| 652 | else if ( edits->downshift ) |
---|
| 653 | { |
---|
| 654 | *p = tolower( *p ); |
---|
| 655 | } |
---|
| 656 | if ( edits->to_slashes ) |
---|
| 657 | { |
---|
| 658 | if ( *p == '\\') |
---|
| 659 | *p = '/'; |
---|
| 660 | } |
---|
| 661 | # ifdef OS_CYGWIN |
---|
| 662 | if ( edits->to_windows ) |
---|
| 663 | { |
---|
| 664 | char result[MAX_PATH + 1]; |
---|
| 665 | cygwin_conv_to_win32_path(out->value, result); |
---|
| 666 | assert(strlen(result) <= MAX_PATH); |
---|
| 667 | string_free( out ); |
---|
| 668 | string_copy( out, result ); |
---|
| 669 | } |
---|
| 670 | # endif |
---|
| 671 | } |
---|
| 672 | out->size = p - out->value; |
---|
| 673 | } |
---|
| 674 | |
---|
| 675 | #ifndef NDEBUG |
---|
| 676 | void var_expand_unit_test() |
---|
| 677 | { |
---|
| 678 | LOL lol[1]; |
---|
| 679 | LIST* l, *l2; |
---|
| 680 | LIST *expected = list_new( list_new( L0, newstr( "axb" ) ), newstr( "ayb" ) ); |
---|
| 681 | LIST *e2; |
---|
| 682 | char axyb[] = "a$(xy)b"; |
---|
| 683 | char azb[] = "a$($(z))b"; |
---|
| 684 | char path[] = "$(p:W)"; |
---|
| 685 | |
---|
| 686 | # ifdef OS_CYGWIN |
---|
| 687 | char cygpath[256]; |
---|
| 688 | cygwin_conv_to_posix_path("c:\\foo\\bar", cygpath); |
---|
| 689 | # else |
---|
| 690 | char cygpath[] = "/cygdrive/c/foo/bar"; |
---|
| 691 | # endif |
---|
| 692 | |
---|
| 693 | lol_init(lol); |
---|
| 694 | var_set("xy", list_new( list_new( L0, newstr( "x" ) ), newstr( "y" ) ), VAR_SET ); |
---|
| 695 | var_set("z", list_new( L0, newstr( "xy" ) ), VAR_SET ); |
---|
| 696 | var_set("p", list_new( L0, newstr( cygpath ) ), VAR_SET ); |
---|
| 697 | |
---|
| 698 | l = var_expand( 0, axyb, axyb + sizeof(axyb) - 1, lol, 0 ); |
---|
| 699 | for ( l2 = l, e2 = expected; l2 && e2; l2 = list_next(l2), e2 = list_next(e2) ) |
---|
| 700 | assert( !strcmp( e2->string, l2->string ) ); |
---|
| 701 | assert(l2 == 0 && e2 == 0); |
---|
| 702 | list_free(l); |
---|
| 703 | |
---|
| 704 | l = var_expand( 0, azb, azb + sizeof(azb) - 1, lol, 0 ); |
---|
| 705 | for ( l2 = l, e2 = expected; l2 && e2; l2 = list_next(l2), e2 = list_next(e2) ) |
---|
| 706 | assert( !strcmp( e2->string, l2->string ) ); |
---|
| 707 | assert(l2 == 0 && e2 == 0); |
---|
| 708 | list_free(l); |
---|
| 709 | |
---|
| 710 | l = var_expand( 0, path, path + sizeof(path) - 1, lol, 0 ); |
---|
| 711 | assert(l != 0); |
---|
| 712 | assert(list_next(l) == 0); |
---|
| 713 | # ifdef OS_CYGWIN |
---|
| 714 | assert( !strcmp( l->string, "c:\\foo\\bar" ) ); |
---|
| 715 | # else |
---|
| 716 | assert( !strcmp( l->string, cygpath ) ); |
---|
| 717 | # endif |
---|
| 718 | list_free(l); |
---|
| 719 | |
---|
| 720 | list_free(expected); |
---|
| 721 | |
---|
| 722 | lol_free(lol); |
---|
| 723 | } |
---|
| 724 | #endif |
---|
| 725 | |
---|
| 726 | /* |
---|
| 727 | Local Variables: |
---|
| 728 | tab-width: 8 |
---|
| 729 | End: |
---|
| 730 | */ |
---|