1/* 2 * Copyright (c) 2010 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of Apple Inc. ("Apple") nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 * Portions of this software have been released under the following terms: 31 * 32 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC. 33 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY 34 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION 35 * 36 * To anyone who acknowledges that this file is provided "AS IS" 37 * without any express or implied warranty: 38 * permission to use, copy, modify, and distribute this file for any 39 * purpose is hereby granted without fee, provided that the above 40 * copyright notices and this notice appears in all source code copies, 41 * and that none of the names of Open Software Foundation, Inc., Hewlett- 42 * Packard Company or Digital Equipment Corporation be used 43 * in advertising or publicity pertaining to distribution of the software 44 * without specific, written prior permission. Neither Open Software 45 * Foundation, Inc., Hewlett-Packard Company nor Digital 46 * Equipment Corporation makes any representations about the suitability 47 * of this software for any purpose. 48 * 49 * Copyright (c) 2007, Novell, Inc. All rights reserved. 50 * Redistribution and use in source and binary forms, with or without 51 * modification, are permitted provided that the following conditions 52 * are met: 53 * 54 * 1. Redistributions of source code must retain the above copyright 55 * notice, this list of conditions and the following disclaimer. 56 * 2. Redistributions in binary form must reproduce the above copyright 57 * notice, this list of conditions and the following disclaimer in the 58 * documentation and/or other materials provided with the distribution. 59 * 3. Neither the name of Novell Inc. nor the names of its contributors 60 * may be used to endorse or promote products derived from this 61 * this software without specific prior written permission. 62 * 63 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 64 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 65 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 66 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY 67 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 68 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 69 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 70 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 71 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 72 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 73 * 74 * @APPLE_LICENSE_HEADER_END@ 75 */ 76 77/* 78** 79** NAME: 80** 81** files.c 82** 83** FACILITY: 84** 85** Interface Definition Language (IDL) Compiler 86** 87** ABSTRACT: 88** 89** IDL file manipulation routines. 90** 91** VERSION: DCE 1.0 92** 93*/ 94 95#include <sys/types.h> 96#include <sys/stat.h> 97 98#include <nidl.h> 99#include <files.h> 100#include <unistd.h> 101#include "message.h" 102 103/* 104** Default filespec; only good for one call to FILE_parse. 105*/ 106char const *FILE_def_filespec = NULL; 107 108/* 109** F I L E _ o p e n 110** 111** Opens an existing file for read access. 112*/ 113 114boolean FILE_open /* Returns TRUE on success */ 115( 116 char *filespec, /* [in] Filespec */ 117 FILE **fid /*[out] File handle; ==NULL on FALSE status */ 118) 119{ 120 if ((*fid = fopen(filespec, "r")) == NULL) 121 { 122 idl_error_list_t errvec[2]; 123 errvec[0].msg_id = NIDL_OPENREAD; 124 errvec[0].arg[0] = filespec; 125 errvec[1].msg_id = NIDL_SYSERRMSG; 126 errvec[1].arg[0] = strerror(errno); 127 error_list(2, errvec, TRUE); 128 } 129 130 return TRUE; 131} 132 133/* 134** F I L E _ c r e a t e 135** 136** Creates and opens a new file for write access. 137*/ 138 139boolean FILE_create /* Returns TRUE on success */ 140( 141 char *filespec, /* [in] Filespec */ 142 FILE **fid /*[out] File handle; ==NULL on FALSE status */ 143) 144{ 145#define MODE_WRITE "w" 146 147 if ((*fid = fopen(filespec, MODE_WRITE)) == NULL) 148 { 149 idl_error_list_t errvec[2]; 150 errvec[0].msg_id = NIDL_OPENWRITE; 151 errvec[0].arg[0] = filespec; 152 errvec[1].msg_id = NIDL_SYSERRMSG; 153 errvec[1].arg[0] = strerror(errno); 154 error_list(2, errvec, TRUE); 155 } 156 157 return TRUE; 158} 159 160/* 161** F I L E _ l o o k u p 162** 163** Looks for the specified file first in the working directory, 164** and then in the list of specified directories. 165** 166** Returns: TRUE if file was found, FALSE otherwise 167*/ 168 169boolean FILE_lookup /* Returns TRUE on success */ 170( 171 char const *filespec, /* [in] Filespec */ 172 char const * const *idir_list, /* [in] Array of directories to search */ 173 /* NULL => just use filespec */ 174 struct stat *stat_buf, /*[out] Stat buffer - see stat.h */ 175 char *lookup_spec, /*[out] Filespec of found file (on success) */ 176 size_t lookup_spec_len /* [in] len of lookup_spec */ 177) 178{ 179#ifdef HASDIRTREE 180 int i; 181 182 /* 183 * First try the filespec by itself. 184 */ 185 if (stat(filespec, stat_buf) != -1) 186 { 187 strlcpy(lookup_spec, filespec, lookup_spec_len); 188 return TRUE; 189 } 190 191 /* 192 * Fail if idir_list is null. 193 */ 194 if (idir_list == NULL) 195 return FALSE; 196 197 /* 198 * Lookup other pathnames using the directories in the idir_list. 199 */ 200 for (i = 0; idir_list[i]; i++) 201 { 202 if (FILE_form_filespec(filespec, idir_list[i], (char *)NULL, 203 (char *)NULL, lookup_spec, lookup_spec_len) 204 && stat(lookup_spec, stat_buf) != -1) 205 return TRUE; 206 } 207 208 /* 209 * On Unix-like filesystems, make another pass over the idir_list if the 210 * search filespec has a directory name, prepending each idir to the search 211 * filespec. For example, importing "y/z.idl" will match "/x/y/z.idl" if 212 * -I/x is on the command line. 213 */ 214 if (*filespec != BRANCHCHAR && FILE_has_dir_info(filespec)) 215 { 216 for (i = 0; idir_list[i]; i++) 217 { 218 sprintf(lookup_spec, "%s%c%s", idir_list[i], BRANCHCHAR, filespec); 219 if (stat(lookup_spec, stat_buf) != -1) 220 return TRUE; 221 } 222 } 223#else 224 error(NIDL_FNUNIXONLY, __FILE__, __LINE__); 225#endif 226 227 return FALSE; 228} 229 230/* 231** F I L E _ f o r m _ f i l e s p e c 232** 233** Forms a file specification from the specified components. 234*/ 235 236boolean FILE_form_filespec /* Returns TRUE on success */ 237( /* For all [in] args, NULL => none */ 238 char const *in_filespec, /* [in] Filespec (full or partial) */ 239 char const *dirspec, /* [in] Directory; used if in_filespec */ 240 /* doesn't have directory field */ 241 char const *type, /* [in] Filetype; used if in_filespec */ 242 /* doesn't have filetype field */ 243 char const *rel_filespec, /* [in] Related filespec; fields are used to */ 244 /* fill in missing components after */ 245 /* applying in_filespec, dir, type */ 246 char *out_filespec, /*[out] Full filespec formed */ 247 size_t out_filespec_len /* [in] len of out_filespec */ 248) 249{ 250 char const *dir = NULL; /* Directory specified */ 251 char in_dir[PATH_MAX]; /* Directory part of in_filespec */ 252 char in_name[PATH_MAX]; /* Filename part of in_filespec */ 253 char in_type[PATH_MAX]; /* Filetype part of in_filespec */ 254 char rel_dir[PATH_MAX]; /* Directory part of rel_filespec */ 255 char rel_name[PATH_MAX]; /* Filename part of rel_filespec */ 256 char rel_type[PATH_MAX]; /* Filetype part of rel_filespec */ 257 char const *res_dir; /* Resultant directory */ 258 char const *res_name; /* Resultant filename */ 259 char const *res_type; /* Resultant filetype */ 260 261 in_dir[0] = '\0'; 262 in_name[0] = '\0'; 263 in_type[0] = '\0'; 264 rel_dir[0] = '\0'; 265 rel_name[0] = '\0'; 266 rel_type[0] = '\0'; 267 res_dir = ""; 268 res_name = ""; 269 res_type = ""; 270 271 /* Parse in_filespec into its components. */ 272 if (in_filespec != NULL && in_filespec[0] != '\0') 273 { 274 /* 275 * Setup the related or file type global FILE_def_filespec such that 276 * any file lookup is handled appropriately in FILE_parse. 277 */ 278 if (rel_filespec) 279 FILE_def_filespec = rel_filespec; 280 else if (type) 281 FILE_def_filespec = type; 282 283 if (!FILE_parse(in_filespec, in_dir, sizeof (in_dir), in_name, sizeof(in_name), in_type, sizeof(in_type))) 284 return FALSE; 285 } 286 287 if (dir == NULL) 288 dir = dirspec; 289 290 /* Parse rel_filespec into its components. */ 291 if (rel_filespec != NULL && rel_filespec[0] != '\0') 292 if (!FILE_parse(rel_filespec, rel_dir, sizeof(rel_dir), rel_name, sizeof(rel_name), rel_type, sizeof(rel_type))) 293 return FALSE; 294 295 /* Apply first valid of in_dir, dir, or rel_dir. */ 296 if (in_dir[0] != '\0') 297 res_dir = in_dir; 298 else if (dir != NULL && dir[0] != '\0') 299 res_dir = dir; 300 else if (rel_dir[0] != '\0') 301 res_dir = rel_dir; 302 303 /* Apply first valid of in_name, rel_name. */ 304 if (in_name[0] != '\0') 305 res_name = in_name; 306 else if (rel_name[0] != '\0') 307 res_name = rel_name; 308 309 /* Apply first valid of in_type, type, rel_type. Note that rel_type is 310 * only applied if in_filespec is null. 311 */ 312 if (in_type[0] != '\0') 313 res_type = in_type; 314 else if (type != NULL && type[0] != '\0') 315 res_type = type; 316 else if (rel_type[0] != '\0') 317 res_type = rel_type; 318 319#ifdef HASDIRTREE 320 321 /* Concatenate the result. */ 322 323 out_filespec[0] = '\0'; 324 325 if (res_dir[0] != '\0') 326 { 327 strlcat(out_filespec, res_dir, out_filespec_len); 328 strlcat(out_filespec, BRANCHSTRING, out_filespec_len); 329 } 330 331 if (res_name[0] != '\0') 332 strlcat(out_filespec, res_name, out_filespec_len); 333 334 if (res_type[0] != '\0') 335 strlcat(out_filespec, res_type, out_filespec_len); /* The '.' is part of the filetype */ 336 337 return TRUE; 338 339#else 340 error(NIDL_FNUNIXONLY, __FILE__, __LINE__); 341#endif 342} 343 344/* 345** F I L E _ p a r s e 346** 347** Parses a specified pathanme into individual components. 348*/ 349 350boolean FILE_parse /* Returns TRUE on success */ 351( 352 char const *filespec, /* [in] Filespec */ 353 char *dir, /*[i,o] Directory portion; NULL =>don't want */ 354 size_t dir_len, /*[i] len of dir */ 355 char *name, /*[i,o] Filename portion; NULL =>don't want */ 356 size_t name_len ATTRIBUTE_UNUSED, /*[i] len of name */ 357 char *type, /*[i,o] File type (ext); NULL =>don't want */ 358 size_t type_len /*[i] len of type */ 359) 360{ 361#if defined(HASDIRTREE) 362 FILE_k_t filekind; /* File kind */ 363 char const *pn; 364 int pn_len, 365 leaf_len; 366 int i, 367 j; 368 int leaf_start, 369 ext_start; 370 int dir_end, 371 leaf_end; 372 boolean slash_seen, 373 dot_seen; 374 375 /* Init return values. */ 376 if (dir) 377 dir[0] = '\0'; 378 if (name) 379 name[0] = '\0'; 380 if (type) 381 type[0] = '\0'; 382 383 /* 384 * If the filespec has BRANCHCHAR do special case check to see if pathname 385 * is a directory to prevent directory /foo/bar from being interpreted as 386 * directory /foo file bar. 387 */ 388 if (strchr(filespec, BRANCHCHAR) 389 && FILE_kind(filespec, &filekind) 390 && filekind == file_dir) 391 { 392 strlcpy(dir, filespec, dir_len); 393 return TRUE; 394 } 395 396 /* 397 * Scan backwards looking for a BRANCHCHAR - 398 * If not found, then no directory was specified. 399 */ 400 pn = filespec; 401 pn_len = strlen(pn); 402 slash_seen = FALSE; 403 dir_end = -1; 404 leaf_start = 0; 405 dot_seen = FALSE; 406 407 /* 408 * For temporary VMS support, until full file capabilities are in place, 409 * look for the defined BRANCHCHAR or a colon, which is indicative of a 410 * device name or logical name. Device and directory information is 411 * collectively returned as the dir argument. 412 */ 413 for (i = pn_len - 1; i >= 0; i--) 414 if (pn[i] == BRANCHCHAR 415#if BRANCHAR == '\\' 416 || pn[i] == '/' 417#endif 418 ) 419 { 420 /* 421 * On VMS, the BRANCHCHAR is considered part of the directory. 422 */ 423 leaf_start = i + 1; 424 dir_end = i > 0 ? i : 1; 425 slash_seen = TRUE; 426 break; 427 } 428 429 if (dir) 430 { 431 if (slash_seen) 432 { 433 strncpy(dir, pn, dir_end); 434 dir[dir_end] = '\0'; 435 } 436 else 437 dir[0] = '\0'; 438 } 439 440 /* 441 * Start scanning from the BRANCHCHAR for a '.' to find the leafname. 442 */ 443 ext_start = pn_len; 444 leaf_end = pn_len; 445 446 for (j = pn_len; j > leaf_start; --j) 447 if (pn[j] == '.') 448 { 449 leaf_end = j - 1; 450 ext_start = j; /* Extension includes the '.' */ 451 dot_seen = TRUE; 452 break; 453 } 454 455 if (leaf_end >= dir_end + 1) 456 { 457 leaf_len = dot_seen ? leaf_end - leaf_start + 1 : leaf_end - leaf_start; 458 if (name) 459 { 460 strncpy(name, &pn[leaf_start], leaf_len); 461 name[leaf_len] = '\0'; 462 } 463 464 if (!dot_seen) 465 { 466 if (type) 467 type[0] = '\0'; 468 return TRUE; 469 } 470 else 471 { 472 if (type) 473 strlcpy(type, &pn[ext_start], type_len); 474 } 475 } 476 477 return TRUE; 478 479#else 480 error(NIDL_FNUNIXONLY, __FILE__, __LINE__); 481 return FALSE; 482#endif 483} 484 485 486/* 487** F I L E _ h a s _ d i r _ i n f o 488** 489** Returns: TRUE if filespec includes directory information. 490*/ 491 492boolean FILE_has_dir_info 493( 494 char const *filespec /* [in] Filespec */ 495) 496{ 497 char dir[PATH_MAX]; /* Directory part of filespec */ 498 499 if (!FILE_parse(filespec, dir, sizeof(dir), (char *)NULL, 0, (char *)NULL, 0)) 500 return FALSE; 501 502 return (dir[0] != '\0'); 503} 504 505/* 506** F I L E _ i s _ c w d 507** 508** Returns: TRUE if filespec is equivalent to the current working directory. 509*/ 510 511boolean FILE_is_cwd 512( 513 char *filespec /* [in] Filespec */ 514) 515{ 516 char *cwd; /* Current working directory */ 517 char *twd; /* Temp working directory = filespec argument */ 518 boolean result; /* Function result */ 519 520 /* Null filespec => current working directory. */ 521 if (filespec[0] == '\0') 522 return TRUE; 523 524 /* Get current working directory. */ 525 cwd = getcwd((char *)NULL, PATH_MAX); 526 if (cwd == NULL) 527 return FALSE; 528 529 /* chdir to the passed directory filespec. */ 530 if (chdir(filespec) != 0) 531 { 532 /* Can chdir; probably a bogus directory. */ 533 free(cwd); 534 return FALSE; 535 } 536 537 /* 538 * Again get current working directory - this gets us the passed 539 * directory filespec in a "normallized form". 540 */ 541 twd = getcwd((char *)NULL, PATH_MAX); 542 if (twd == NULL) 543 { 544 free(cwd); 545 return FALSE; 546 } 547 548 if (strcmp(cwd, twd) == 0) 549 result = TRUE; 550 else 551 { 552 /* Not current working directory; be sure to chdir back to original! */ 553 result = FALSE; 554 chdir(cwd); 555 } 556 557 /* Free storage malloc'ed by getcwd(). */ 558 free(cwd); 559 free(twd); 560 561 return result; 562} 563 564/* 565** F I L E _ k i n d 566** 567** Returns whether a pathname is a directory, a file, or something else. 568*/ 569 570boolean FILE_kind /* Returns TRUE on success */ 571( 572 char const *filespec, /* [in] Filespec */ 573 FILE_k_t *filekind /*[out] File kind (on success) */ 574) 575{ 576 struct stat fileinfo; 577 578 if (stat(filespec, &fileinfo) == -1) 579 return FALSE; 580 581 switch (fileinfo.st_mode & S_IFMT) 582 { 583 case S_IFDIR: 584 *filekind = file_dir; 585 break; 586 587 case S_IFREG: 588 *filekind = file_file; 589 break; 590 591 default: 592 *filekind = file_special; 593 } 594 595 return TRUE; 596} 597 598/* 599** F I L E _ c o n t a i n s _ e v _ r e f 600** 601** Scans a pathname to see if it contains an environment variable reference. 602*/ 603 604boolean FILE_contains_ev_ref /* Returns TRUE if filespec contains an */ 605 /* environment variable reference */ 606( 607 STRTAB_str_t fs_id /* [in] Filespec stringtable ID */ 608) 609{ 610 char const *pn; 611 unsigned int i; 612 613 STRTAB_str_to_string(fs_id, &pn); 614 615 for (i = 0; i < strlen(pn) - 1; i++) 616 if (pn[i] == '$' && pn[i + 1] == '(') 617 return TRUE; 618 619 return FALSE; 620} 621 622/* 623** F I L E _ e x e c u t e _ c m d 624** 625** This routine executes the specified command string with 626** the specified parameters. All error output goes to the 627** default output/error device. 628*/ 629 630int FILE_execute_cmd 631( 632 char *cmd_string, /* command to execute */ 633 char *p1, /* parameter1 */ 634 char *p2, /* parameter2 */ 635 long msg_id /* Optional msg_id to output */ 636) 637{ 638 char *cmd; /* Command derived from inputs */ 639 int status; 640 size_t cmd_len = 0; 641 642 /* Alloc space and create command string */ 643 cmd_len = strlen(cmd_string) + strlen(p1) + strlen(p2) + 3; 644 cmd = NEW_VEC (char, cmd_len); 645 cmd[0] = '\0'; 646 strlcat(cmd, cmd_string, cmd_len); 647 strlcat(cmd, " ", cmd_len); 648 strlcat(cmd, p1, cmd_len); 649 strlcat(cmd, " ", cmd_len); 650 strlcat(cmd, p2, cmd_len); 651 652 /* Output a message, if msg_id specified is non-zero */ 653 if (msg_id != 0) 654 message_print(msg_id, (char*)cmd); 655 656 /* Execute the command, errors to default output device */ 657 status = system(cmd); 658 659 /* Free the command string */ 660 FREE(cmd); 661 662 return status; 663} 664 665/* 666** F I L E _ d e l e t e 667** 668** This routine deletes the file specified by the filename 669** string specified. 670*/ 671 672void FILE_delete 673( 674 char *filename 675) 676{ 677 unlink (filename); 678} 679/* preserve coding style vim: set tw=78 sw=4 : */ 680