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