1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1995 Sun Microsystems, Inc. All Rights Reserved 24 * 25 * module: 26 * files.c 27 * 28 * purpose: 29 * routines to examine and manipulate file names 30 * 31 * contents: 32 * qualify ... ensure that a name is fully qualified 33 * expand ... expand env variables within a string or file name 34 * noblanks .. ensure that a name contains no embdded unescaped blanks 35 * lex ....... a lexer that can handle escaped/embedded blanks 36 * wildcards . see whether or not a name contains wild cards 37 * prefix .... does one string begin with another 38 * suffix .... does one string end with another 39 * contains .. does one string contain another 40 * 41 * cannonize (static) ... compress redundant "." and ".." out of name 42 * 43 * notes: 44 * we are interested in embedded blanks because international 45 * character sets and non-unix file systems can both contain 46 * the byte 0x20. Thus, whenever we record a filename in 47 * file, we must be careful to escape any embedded blanks that 48 * cause trouble when we re-lex that file later. 49 */ 50 #ident "%W% %E% SMI" 51 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <ctype.h> 56 #include <unistd.h> 57 58 #include "filesync.h" 59 #include "messages.h" 60 61 static void cannonize(char *name); 62 63 /* 64 * routine: 65 * qualify 66 * 67 * purpose: 68 * to fully qualify a name 69 * 70 * parameters: 71 * name to be qualified 72 * 73 * returns: 74 * either original pointer or copy to a new (malloced) buffer 75 * 76 * notes: 77 * someday I may conclude that I should always make a copy 78 * so that the caller can know that it is safe to free the parm 79 * 80 * I thought about this and concluded that there is never a need 81 * to fully qualify a string containing variables. If the string 82 * came from the command line, the variables were already expanded 83 * and if it came from the rules data base it is required to already 84 * be fully qualified. 85 */ 86 char * 87 qualify(char *name) 88 { 89 char namebuf[ MAX_PATH ]; 90 91 /* in the simple case, the parameter is already there */ 92 if (*name == '/') { 93 cannonize(name); 94 return (name); 95 } 96 97 /* things that begin with variables get the benefit of the doubt */ 98 if (*name == '$') { 99 cannonize(name); 100 return (name); 101 } 102 103 /* start with the current working directory */ 104 if (getcwd(namebuf, sizeof (namebuf)) == 0) { 105 fprintf(stderr, gettext(ERR_nocwd), name); 106 exit(ERR_OTHER); 107 } 108 109 /* make sure we have room for our file name */ 110 if ((strlen(namebuf) + strlen(name) + 2) >= sizeof (namebuf)) { 111 fprintf(stderr, gettext(ERR_longname), name); 112 exit(ERR_OTHER); 113 } 114 115 /* append the specified file name to it */ 116 strcat(namebuf, "/"); 117 strcat(namebuf, name); 118 119 /* filter out redundant dots */ 120 cannonize(namebuf); 121 122 if (opt_debug & DBG_VARS) 123 fprintf(stderr, "VARS: QUALIFY %s to %s\n", name, namebuf); 124 125 /* and return a newly malloc'd copy */ 126 return (strdup(namebuf)); 127 } 128 129 /* 130 * routine: 131 * expand 132 * 133 * purpose: 134 * to expand variable names within a string 135 * 136 * parameters: 137 * string to be expanded. Variable references always begin 138 * with a $ and are delimited by parens or curleys. 139 * 140 * returns: 141 * either original pointer or a copy to a new (malloced) buffer 142 * 143 * notes: 144 * someday I may conclude that I should always make a copy 145 * so that the caller can know that it is safe to free the parm 146 * 147 * someday I may decide to support escape conventions for embedding 148 * $(){} in file names, but I suspec that day will never come. 149 * 150 * I thought about this and concluded there was no reason to 151 * fully qualify these names, because the only names that should 152 * need qualification are src/dst lines from the command line, 153 * and the shell should have handled those for me. Once something 154 * makes it into the database, it is expected to be fully qualified 155 * already. 156 * 157 * We are limited to producing strings of length MAX_PATH or less 158 * and variable names of length MAX_NAME or less. In practice, 159 * these limitations should not be a problem. 160 */ 161 char * 162 expand(char *name) 163 { const char *s; 164 char *p, *v; 165 char delim; 166 char namebuf[ MAX_PATH ]; 167 char varbuf[ MAX_NAME ]; 168 169 /* first see if there are no variables to be bound */ 170 for (s = name; *s && *s != '$'; s++); 171 if (*s == 0) 172 return (name); 173 174 /* move through the string, copying and expanding */ 175 for (s = name, p = namebuf; *s; s++) { 176 177 /* check for overflow */ 178 if (p >= &namebuf[ MAX_PATH ]) { 179 fprintf(stderr, gettext(ERR_longname), name); 180 exit(ERR_OTHER); 181 } 182 183 /* normal characters, we just copy */ 184 if (*s != '$') { 185 *p++ = *s; 186 continue; 187 } 188 189 /* figure out how the variable name is delimited */ 190 delim = *++s; 191 if (delim == '(') { 192 delim = ')'; 193 s++; 194 } else if (delim == '{') { 195 delim = '}'; 196 s++; 197 } else 198 delim = 0; 199 200 /* copy the variable name up to the closing delimiter */ 201 for (v = varbuf; *s; s++) { 202 if (isalnum(*s) || (*s == '_') || 203 (delim && *s != delim)) 204 *v++ = *s; 205 else 206 break; 207 208 /* make sure we don't overflow var name buffer */ 209 if (v >= &varbuf[MAX_NAME - 1]) { 210 *v = 0; 211 fprintf(stderr, gettext(ERR_longname), varbuf); 212 exit(ERR_OTHER); 213 } 214 } 215 216 *v = 0; 217 218 /* FIX THIS ... there must be a more elegant way */ 219 /* we may have to back up because s will be bumped */ 220 if (delim == 0 || *s != delim) 221 s--; 222 223 /* look up the variable */ 224 v = getenv(varbuf); 225 if (v == 0 || *v == 0) { 226 fprintf(stderr, gettext(ERR_undef), varbuf); 227 return (0); 228 } 229 230 /* copy the variable into the buffer */ 231 while (*v) 232 *p++ = *v++; 233 } 234 235 /* null terminate the copy */ 236 *p = 0; 237 238 /* compress out any redundant dots and dot-dots */ 239 cannonize(namebuf); 240 241 if (opt_debug & DBG_VARS) 242 fprintf(stderr, "VARS: EXPAND %s to %s\n", name, namebuf); 243 244 /* and return a newly malloc'd copy */ 245 return (strdup(namebuf)); 246 } 247 248 /* 249 * routine: 250 * noblanks 251 * 252 * purpose: 253 * to ensure that a name contains no unescaped embedded blanks 254 * 255 * parameters: 256 * pointer to name 257 * 258 * returns: 259 * pointer to name or pointer to buffer containing escaped version of name 260 * 261 * notes: 262 * this routine can be called on full file names, and so can 263 * conceivably require an arbitrarily large buffer. 264 */ 265 const char * 266 noblanks(const char *name) 267 { 268 const char *s; 269 char *p; 270 static char *namebuf = 0; 271 static int buflen = 0; 272 int l; 273 274 /* first see if there are no embedded blanks */ 275 for (s = name; *s && *s != ' '; s++); 276 if (*s == 0) 277 return (name); 278 279 /* make sure we have a buffer large enough for the worst case */ 280 l = 4 + (2*strlen(name)); 281 for (buflen = MAX_PATH; buflen < l; buflen += MAX_NAME); 282 namebuf = (char *) realloc(namebuf, buflen); 283 284 /* quote the name, and copy it, escaping quotes */ 285 p = namebuf; 286 *p++ = '"'; 287 288 for (s = name; *s; s++) { 289 if (*s == '"' || *s == '\\') 290 *p++ = '\\'; 291 *p++ = *s; 292 } 293 294 *p++ = '"'; 295 *p = 0; 296 297 return (namebuf); 298 } 299 300 /* 301 * routine: 302 * lex 303 * 304 * purpose: 305 * my own version of strtok that handles quoting and escaping 306 * 307 * parameters: 308 * FILE structure for file to read (0 for same string, same file) 309 * 310 * returns: 311 * pointer to next token 312 * 313 * notes: 314 * this routine makes no changes to the string it is passed, 315 * copying tokens into a static buffer. 316 * 317 * this routine handles continuation lines after reading and 318 * before the lexing even starts. This limits continued lines 319 * to a length of MAX_LINE, but keeps everything else very simple. 320 * We also, therefore, limit tokens to a maximum length of MAX_LINE. 321 */ 322 int lex_linenum; /* line number in current input file */ 323 324 char * 325 lex(FILE *file) 326 { char c, delim; 327 char *p; 328 char *s; 329 static char *savep; 330 static char namebuf[ MAX_LINE ]; 331 static char inbuf[ MAX_LINE ]; 332 333 if (file) { /* read a new line */ 334 p = inbuf + sizeof (inbuf); 335 336 /* read the next input line, with all continuations */ 337 for (s = inbuf; savep = fgets(s, p - s, file); ) { 338 lex_linenum++; 339 340 /* go find the last character of the input line */ 341 while (*s && s[1]) 342 s++; 343 if (*s == '\n') 344 s--; 345 346 /* see whether or not we need a continuation */ 347 if (s < inbuf || *s != '\\') 348 break; 349 350 continue; 351 } 352 353 if (savep == 0) 354 return (0); 355 356 s = inbuf; 357 } else { /* continue with old line */ 358 if (savep == 0) 359 return (0); 360 s = savep; 361 } 362 savep = 0; 363 364 /* skip over leading white space */ 365 while (isspace(*s)) 366 s++; 367 if (*s == 0) 368 return (0); 369 370 /* see if this is a quoted string */ 371 c = *s; 372 if (c == '\'' || c == '"') { 373 delim = c; 374 s++; 375 } else 376 delim = 0; 377 378 /* copy the token into the buffer */ 379 for (p = namebuf; (c = *s) != 0; s++) { 380 /* literal escape */ 381 if (c == '\\') { 382 s++; 383 *p++ = *s; 384 continue; 385 } 386 387 /* closing delimiter */ 388 if (c == delim) { 389 s++; 390 break; 391 } 392 393 /* delimiting white space */ 394 if (delim == 0 && isspace(c)) 395 break; 396 397 /* ordinary characters */ 398 *p++ = *s; 399 } 400 401 402 /* remember where we left off */ 403 savep = *s ? s : 0; 404 405 /* null terminate and return the buffer */ 406 *p = 0; 407 return (namebuf); 408 } 409 410 /* 411 * routine: 412 * wildcards 413 * 414 * purpose: 415 * determine whether or not there are any wild cards in a name 416 * 417 * parameters: 418 * name to be checked 419 * 420 * returns: 421 * true/false 422 * 423 * notes: 424 * we use this to take shortcuts 425 */ 426 bool_t 427 wildcards(const char *name) 428 { const char *s; 429 int literal = 0; 430 431 for (s = name; *s; s++) 432 if (literal) 433 switch (*s) { 434 case '\'': /* end of literal string */ 435 literal = 0; 436 continue; 437 case '\\': /* escape next character */ 438 s++; 439 continue; 440 } 441 else 442 switch (*s) { 443 case '\'': /* literal string */ 444 literal = 1; 445 continue; 446 case '\\': /* escape next character */ 447 s++; 448 continue; 449 case '*': 450 case '[': 451 case '{': 452 case '?': 453 /* any of these is a wild card */ 454 return (TRUE); 455 } 456 457 return (FALSE); 458 } 459 460 /* 461 * routine: 462 * cannonize 463 * 464 * purpose: 465 * to compress redundant dots out of a path 466 * 467 * parameters: 468 * file name in an editable buffer 469 * 470 * returns: 471 * void 472 * 473 * notes: 474 * because we compress the string in place, there is no danger 475 * of our overflowing any fixed sized buffer. 476 */ 477 static void 478 cannonize(char *name) 479 { char *s, *p; 480 481 /* leading dot-slashes */ 482 for (s = name; *s == '.' && s[1] == '/'; strcpy(s, &s[2])); 483 484 for (s = name; *s; s++) { 485 /* interesting things happen after slashes */ 486 if (*s != '/') 487 continue; 488 489 /* embedded dot-slashes */ 490 while (s[1] == '.' && s[2] == '/') 491 strcpy(&s[1], &s[3]); 492 493 /* embedded slash-dot-dot-slash */ 494 if (strncmp(s, "/../", 4) == 0) { 495 /* scan backwards to eliminate last directory */ 496 for (p = s-1; p > name && *p != '/'; p--); 497 498 if (p < name) 499 p = name; 500 strcpy(p, &s[3]); 501 } 502 503 continue; 504 } 505 } 506 507 /* 508 * routine: 509 * prefix 510 * 511 * purpose: 512 * determine whether or not one string begins with another 513 * 514 * parameters: 515 * string to be tested 516 * suspected prefix 517 * 518 * returns: 519 * no 0 520 * yes pointer character after prefix 521 */ 522 const char * 523 prefix(const char *s, const char *p) 524 { 525 while (*p) 526 if (*p++ != *s++) 527 return (0); 528 529 return (s); 530 } 531 532 /* 533 * routine: 534 * suffix 535 * 536 * purpose: 537 * determine whether or not one string ends with another 538 * 539 * parameters: 540 * string to be tested 541 * suspected suffix 542 * 543 * returns: 544 * true/false 545 */ 546 bool_t 547 suffix(const char *str, const char *suf) 548 { const char *s; 549 550 /* go to where the alleged suffix would start */ 551 for (s = str; *s; s++); 552 s -= strlen(suf); 553 if (s < str) 554 return (FALSE); 555 556 /* see if the string ends with the suffix */ 557 while (*suf) 558 if (*suf++ != *s++) 559 return (FALSE); 560 561 return (TRUE); 562 } 563 564 /* 565 * routine: 566 * contains 567 * 568 * purpose: 569 * determine whether or not one string contains another 570 * 571 * parameters: 572 * string to be checked 573 * pattern we are seeking 574 * 575 * returns: 576 * true/false 577 */ 578 bool_t 579 contains(const char *str, const char *pat) 580 { const char *s, *p; 581 582 while (*str) { 583 if (*str++ == *pat) { 584 for (s = str, p = &pat[1]; *s == *p; s++, p++) 585 if (p[1] == 0) 586 return (TRUE); 587 } 588 } 589 590 return (FALSE); 591 }