OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [tcl/] [unix/] [tclUnixFCmd.c] - Blame information for rev 1774

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
2
 * tclUnixFCmd.c
3
 *
4
 *      This file implements the unix specific portion of file manipulation
5
 *      subcommands of the "file" command.  All filename arguments should
6
 *      already be translated to native format.
7
 *
8
 * Copyright (c) 1996-1997 Sun Microsystems, Inc.
9
 *
10
 * See the file "license.terms" for information on usage and redistribution
11
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12
 *
13
 * RCS: @(#) $Id: tclUnixFCmd.c,v 1.1.1.1 2002-01-16 10:25:37 markom Exp $
14
 *
15
 * Portions of this code were derived from NetBSD source code which has
16
 * the following copyright notice:
17
 *
18
 * Copyright (c) 1988, 1993, 1994
19
 *      The Regents of the University of California.  All rights reserved.
20
 *
21
 * Redistribution and use in source and binary forms, with or without
22
 * modification, are permitted provided that the following conditions
23
 * are met:
24
 * 1. Redistributions of source code must retain the above copyright
25
 *    notice, this list of conditions and the following disclaimer.
26
 * 2. Redistributions in binary form must reproduce the above copyright
27
 *    notice, this list of conditions and the following disclaimer in the
28
 *    documentation and/or other materials provided with the distribution.
29
 * 3. All advertising materials mentioning features or use of this software
30
 *    must display the following acknowledgement:
31
 *      This product includes software developed by the University of
32
 *      California, Berkeley and its contributors.
33
 * 4. Neither the name of the University nor the names of its contributors
34
 *    may be used to endorse or promote products derived from this software
35
 *    without specific prior written permission.
36
 *
37
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
38
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
41
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
42
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
43
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
45
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
46
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47
 * SUCH DAMAGE.
48
 */
49
 
50
#include "tclInt.h"
51
#include "tclPort.h"
52
#include <utime.h>
53
#include <grp.h>
54
 
55
/*
56
 * The following constants specify the type of callback when
57
 * TraverseUnixTree() calls the traverseProc()
58
 */
59
 
60
#define DOTREE_PRED   1     /* pre-order directory  */
61
#define DOTREE_POSTD  2     /* post-order directory */
62
#define DOTREE_F      3     /* regular file */
63
 
64
/*
65
 * Callbacks for file attributes code.
66
 */
67
 
68
static int              GetGroupAttribute _ANSI_ARGS_((Tcl_Interp *interp,
69
                            int objIndex, char *fileName,
70
                            Tcl_Obj **attributePtrPtr));
71
static int              GetOwnerAttribute _ANSI_ARGS_((Tcl_Interp *interp,
72
                            int objIndex, char *fileName,
73
                            Tcl_Obj **attributePtrPtr));
74
static int              GetPermissionsAttribute _ANSI_ARGS_((
75
                            Tcl_Interp *interp, int objIndex, char *fileName,
76
                            Tcl_Obj **attributePtrPtr));
77
static int              SetGroupAttribute _ANSI_ARGS_((Tcl_Interp *interp,
78
                            int objIndex, char *fileName,
79
                            Tcl_Obj *attributePtr));
80
static int              SetOwnerAttribute _ANSI_ARGS_((Tcl_Interp *interp,
81
                            int objIndex, char *fileName,
82
                            Tcl_Obj *attributePtr));
83
static int              SetPermissionsAttribute _ANSI_ARGS_((
84
                            Tcl_Interp *interp, int objIndex, char *fileName,
85
                            Tcl_Obj *attributePtr));
86
 
87
/*
88
 * Prototype for the TraverseUnixTree callback function.
89
 */
90
 
91
typedef int (TraversalProc) _ANSI_ARGS_((char *src, char *dst,
92
        struct stat *sb, int type, Tcl_DString *errorPtr));
93
 
94
/*
95
 * Constants and variables necessary for file attributes subcommand.
96
 */
97
 
98
enum {
99
    UNIX_GROUP_ATTRIBUTE,
100
    UNIX_OWNER_ATTRIBUTE,
101
    UNIX_PERMISSIONS_ATTRIBUTE
102
};
103
 
104
char *tclpFileAttrStrings[] = {"-group", "-owner", "-permissions",
105
        (char *) NULL};
106
CONST TclFileAttrProcs tclpFileAttrProcs[] = {
107
        {GetGroupAttribute, SetGroupAttribute},
108
        {GetOwnerAttribute, SetOwnerAttribute},
109
        {GetPermissionsAttribute, SetPermissionsAttribute}};
110
 
111
/*
112
 * Declarations for local procedures defined in this file:
113
 */
114
 
115
static int              CopyFile _ANSI_ARGS_((char *src, char *dst,
116
                            struct stat *srcStatBufPtr));
117
static int              CopyFileAtts _ANSI_ARGS_((char *src, char *dst,
118
                            struct stat *srcStatBufPtr));
119
static int              TraversalCopy _ANSI_ARGS_((char *src, char *dst,
120
                            struct stat *sbPtr, int type,
121
                            Tcl_DString *errorPtr));
122
static int              TraversalDelete _ANSI_ARGS_((char *src, char *dst,
123
                            struct stat *sbPtr, int type,
124
                            Tcl_DString *errorPtr));
125
static int              TraverseUnixTree _ANSI_ARGS_((
126
                            TraversalProc *traversalProc,
127
                            Tcl_DString *sourcePath, Tcl_DString *destPath,
128
                            Tcl_DString *errorPtr));
129
 
130
/*
131
 *---------------------------------------------------------------------------
132
 *
133
 * TclpRenameFile --
134
 *
135
 *      Changes the name of an existing file or directory, from src to dst.
136
 *      If src and dst refer to the same file or directory, does nothing
137
 *      and returns success.  Otherwise if dst already exists, it will be
138
 *      deleted and replaced by src subject to the following conditions:
139
 *          If src is a directory, dst may be an empty directory.
140
 *          If src is a file, dst may be a file.
141
 *      In any other situation where dst already exists, the rename will
142
 *      fail.
143
 *
144
 * Results:
145
 *      If the directory was successfully created, returns TCL_OK.
146
 *      Otherwise the return value is TCL_ERROR and errno is set to
147
 *      indicate the error.  Some possible values for errno are:
148
 *
149
 *      EACCES:     src or dst parent directory can't be read and/or written.
150
 *      EEXIST:     dst is a non-empty directory.
151
 *      EINVAL:     src is a root directory or dst is a subdirectory of src.
152
 *      EISDIR:     dst is a directory, but src is not.
153
 *      ENOENT:     src doesn't exist, or src or dst is "".
154
 *      ENOTDIR:    src is a directory, but dst is not.
155
 *      EXDEV:      src and dst are on different filesystems.
156
 *
157
 * Side effects:
158
 *      The implementation of rename may allow cross-filesystem renames,
159
 *      but the caller should be prepared to emulate it with copy and
160
 *      delete if errno is EXDEV.
161
 *
162
 *---------------------------------------------------------------------------
163
 */
164
 
165
int
166
TclpRenameFile(src, dst)
167
    char *src;                  /* Pathname of file or dir to be renamed. */
168
    char *dst;                  /* New pathname of file or directory. */
169
{
170
    if (rename(src, dst) == 0) {
171
        return TCL_OK;
172
    }
173
    if (errno == ENOTEMPTY) {
174
        errno = EEXIST;
175
    }
176
 
177
#ifdef sparc
178
    /*
179
     * SunOS 4.1.4 reports overwriting a non-empty directory with a
180
     * directory as EINVAL instead of EEXIST (first rule out the correct
181
     * EINVAL result code for moving a directory into itself).  Must be
182
     * conditionally compiled because realpath() is only defined on SunOS.
183
     */
184
 
185
    if (errno == EINVAL) {
186
        char srcPath[MAXPATHLEN], dstPath[MAXPATHLEN];
187
        DIR *dirPtr;
188
        struct dirent *dirEntPtr;
189
 
190
        if ((realpath(src, srcPath) != NULL)
191
                && (realpath(dst, dstPath) != NULL)
192
                && (strncmp(srcPath, dstPath, strlen(srcPath)) != 0)) {
193
            dirPtr = opendir(dst);
194
            if (dirPtr != NULL) {
195
                while ((dirEntPtr = readdir(dirPtr)) != NULL) {
196
                    if ((strcmp(dirEntPtr->d_name, ".") != 0) &&
197
                            (strcmp(dirEntPtr->d_name, "..") != 0)) {
198
                        errno = EEXIST;
199
                        closedir(dirPtr);
200
                        return TCL_ERROR;
201
                    }
202
                }
203
                closedir(dirPtr);
204
            }
205
        }
206
        errno = EINVAL;
207
    }
208
#endif  /* sparc */
209
 
210
    if (strcmp(src, "/") == 0) {
211
        /*
212
         * Alpha reports renaming / as EBUSY and Linux reports it as EACCES,
213
         * instead of EINVAL.
214
         */
215
 
216
        errno = EINVAL;
217
    }
218
 
219
    /*
220
     * DEC Alpha OSF1 V3.0 returns EACCES when attempting to move a
221
     * file across filesystems and the parent directory of that file is
222
     * not writable.  Most other systems return EXDEV.  Does nothing to
223
     * correct this behavior.
224
     */
225
 
226
    return TCL_ERROR;
227
}
228
 
229
 
230
/*
231
 *---------------------------------------------------------------------------
232
 *
233
 * TclpCopyFile --
234
 *
235
 *      Copy a single file (not a directory).  If dst already exists and
236
 *      is not a directory, it is removed.
237
 *
238
 * Results:
239
 *      If the file was successfully copied, returns TCL_OK.  Otherwise
240
 *      the return value is TCL_ERROR and errno is set to indicate the
241
 *      error.  Some possible values for errno are:
242
 *
243
 *      EACCES:     src or dst parent directory can't be read and/or written.
244
 *      EISDIR:     src or dst is a directory.
245
 *      ENOENT:     src doesn't exist.  src or dst is "".
246
 *
247
 * Side effects:
248
 *      This procedure will also copy symbolic links, block, and
249
 *      character devices, and fifos.  For symbolic links, the links
250
 *      themselves will be copied and not what they point to.  For the
251
 *      other special file types, the directory entry will be copied and
252
 *      not the contents of the device that it refers to.
253
 *
254
 *---------------------------------------------------------------------------
255
 */
256
 
257
int
258
TclpCopyFile(src, dst)
259
    char *src;                  /* Pathname of file to be copied. */
260
    char *dst;                  /* Pathname of file to copy to. */
261
{
262
    struct stat srcStatBuf, dstStatBuf;
263
    char link[MAXPATHLEN];
264
    int length;
265
 
266
    /*
267
     * Have to do a stat() to determine the filetype.
268
     */
269
 
270
    if (lstat(src, &srcStatBuf) != 0) {
271
        return TCL_ERROR;
272
    }
273
    if (S_ISDIR(srcStatBuf.st_mode)) {
274
        errno = EISDIR;
275
        return TCL_ERROR;
276
    }
277
 
278
    /*
279
     * symlink, and some of the other calls will fail if the target
280
     * exists, so we remove it first
281
     */
282
 
283
    if (lstat(dst, &dstStatBuf) == 0) {
284
        if (S_ISDIR(dstStatBuf.st_mode)) {
285
            errno = EISDIR;
286
            return TCL_ERROR;
287
        }
288
    }
289
    if (unlink(dst) != 0) {
290
        if (errno != ENOENT) {
291
            return TCL_ERROR;
292
        }
293
    }
294
 
295
    switch ((int) (srcStatBuf.st_mode & S_IFMT)) {
296
        case S_IFLNK:
297
            length = readlink(src, link, sizeof(link));
298
            if (length == -1) {
299
                return TCL_ERROR;
300
            }
301
            link[length] = '\0';
302
            if (symlink(link, dst) < 0) {
303
                return TCL_ERROR;
304
            }
305
            break;
306
 
307
        case S_IFBLK:
308
        case S_IFCHR:
309
            if (mknod(dst, srcStatBuf.st_mode, srcStatBuf.st_rdev) < 0) {
310
                return TCL_ERROR;
311
            }
312
            return CopyFileAtts(src, dst, &srcStatBuf);
313
 
314
#ifndef __CYGWIN__
315
        case S_IFIFO:
316
            if (mkfifo(dst, srcStatBuf.st_mode) < 0) {
317
                return TCL_ERROR;
318
            }
319
            return CopyFileAtts(src, dst, &srcStatBuf);
320
#endif
321
 
322
        default:
323
            return CopyFile(src, dst, &srcStatBuf);
324
    }
325
 
326
    return TCL_OK;
327
}
328
 
329
/*
330
 *----------------------------------------------------------------------
331
 *
332
 * CopyFile -
333
 *
334
 *      Helper function for TclpCopyFile.  Copies one regular file,
335
 *      using read() and write().
336
 *
337
 * Results:
338
 *      A standard Tcl result.
339
 *
340
 * Side effects:
341
 *      A file is copied.  Dst will be overwritten if it exists.
342
 *
343
 *----------------------------------------------------------------------
344
 */
345
 
346
static int
347
CopyFile(src, dst, srcStatBufPtr)
348
    char *src;                   /* Pathname of file to copy. */
349
    char *dst;                   /* Pathname of file to create/overwrite. */
350
    struct stat *srcStatBufPtr;  /* Used to determine mode and blocksize */
351
{
352
    int srcFd;
353
    int dstFd;
354
    u_int blockSize;   /* Optimal I/O blocksize for filesystem */
355
    char *buffer;      /* Data buffer for copy */
356
    size_t nread;
357
 
358
    if ((srcFd = open(src, O_RDONLY, 0)) < 0) {
359
        return TCL_ERROR;
360
    }
361
 
362
    dstFd = open(dst, O_CREAT | O_TRUNC | O_WRONLY, srcStatBufPtr->st_mode);
363
    if (dstFd < 0) {
364
        close(srcFd);
365
        return TCL_ERROR;
366
    }
367
 
368
#if HAVE_ST_BLKSIZE
369
    blockSize = srcStatBufPtr->st_blksize;
370
#else
371
    blockSize = 4096;
372
#endif
373
 
374
    buffer = ckalloc(blockSize);
375
    while (1) {
376
        nread = read(srcFd, buffer, blockSize);
377
        if ((nread == -1) || (nread == 0)) {
378
            break;
379
        }
380
        if (write(dstFd, buffer, nread) != nread) {
381
            nread = (size_t) -1;
382
            break;
383
        }
384
    }
385
 
386
    ckfree(buffer);
387
    close(srcFd);
388
    if ((close(dstFd) != 0) || (nread == -1)) {
389
        unlink(dst);
390
        return TCL_ERROR;
391
    }
392
    if (CopyFileAtts(src, dst, srcStatBufPtr) == TCL_ERROR) {
393
        /*
394
         * The copy succeeded, but setting the permissions failed, so be in
395
         * a consistent state, we remove the file that was created by the
396
         * copy.
397
         */
398
 
399
        unlink(dst);
400
        return TCL_ERROR;
401
    }
402
    return TCL_OK;
403
}
404
 
405
/*
406
 *---------------------------------------------------------------------------
407
 *
408
 * TclpDeleteFile --
409
 *
410
 *      Removes a single file (not a directory).
411
 *
412
 * Results:
413
 *      If the file was successfully deleted, returns TCL_OK.  Otherwise
414
 *      the return value is TCL_ERROR and errno is set to indicate the
415
 *      error.  Some possible values for errno are:
416
 *
417
 *      EACCES:     a parent directory can't be read and/or written.
418
 *      EISDIR:     path is a directory.
419
 *      ENOENT:     path doesn't exist or is "".
420
 *
421
 * Side effects:
422
 *      The file is deleted, even if it is read-only.
423
 *
424
 *---------------------------------------------------------------------------
425
 */
426
 
427
int
428
TclpDeleteFile(path)
429
    char *path;                 /* Pathname of file to be removed. */
430
{
431
    if (unlink(path) != 0) {
432
        return TCL_ERROR;
433
    }
434
    return TCL_OK;
435
}
436
 
437
/*
438
 *---------------------------------------------------------------------------
439
 *
440
 * TclpCreateDirectory --
441
 *
442
 *      Creates the specified directory.  All parent directories of the
443
 *      specified directory must already exist.  The directory is
444
 *      automatically created with permissions so that user can access
445
 *      the new directory and create new files or subdirectories in it.
446
 *
447
 * Results:
448
 *      If the directory was successfully created, returns TCL_OK.
449
 *      Otherwise the return value is TCL_ERROR and errno is set to
450
 *      indicate the error.  Some possible values for errno are:
451
 *
452
 *      EACCES:     a parent directory can't be read and/or written.
453
 *      EEXIST:     path already exists.
454
 *      ENOENT:     a parent directory doesn't exist.
455
 *
456
 * Side effects:
457
 *      A directory is created with the current umask, except that
458
 *      permission for u+rwx will always be added.
459
 *
460
 *---------------------------------------------------------------------------
461
 */
462
 
463
int
464
TclpCreateDirectory(path)
465
    char *path;                 /* Pathname of directory to create. */
466
{
467
    mode_t mode;
468
 
469
    mode = umask(0);
470
    umask(mode);
471
 
472
    /*
473
     * umask return value is actually the inverse of the permissions.
474
     */
475
 
476
    mode = (0777 & ~mode);
477
 
478
    if (mkdir(path, mode | S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
479
        return TCL_ERROR;
480
    }
481
    return TCL_OK;
482
}
483
 
484
/*
485
 *---------------------------------------------------------------------------
486
 *
487
 * TclpCopyDirectory --
488
 *
489
 *      Recursively copies a directory.  The target directory dst must
490
 *      not already exist.  Note that this function does not merge two
491
 *      directory hierarchies, even if the target directory is an an
492
 *      empty directory.
493
 *
494
 * Results:
495
 *      If the directory was successfully copied, returns TCL_OK.
496
 *      Otherwise the return value is TCL_ERROR, errno is set to indicate
497
 *      the error, and the pathname of the file that caused the error
498
 *      is stored in errorPtr.  See TclpCreateDirectory and TclpCopyFile
499
 *      for a description of possible values for errno.
500
 *
501
 * Side effects:
502
 *      An exact copy of the directory hierarchy src will be created
503
 *      with the name dst.  If an error occurs, the error will
504
 *      be returned immediately, and remaining files will not be
505
 *      processed.
506
 *
507
 *---------------------------------------------------------------------------
508
 */
509
 
510
int
511
TclpCopyDirectory(src, dst, errorPtr)
512
    char *src;                  /* Pathname of directory to be copied.  */
513
    char *dst;                  /* Pathname of target directory. */
514
    Tcl_DString *errorPtr;      /* If non-NULL, initialized DString for
515
                                 * error reporting. */
516
{
517
    int result;
518
    Tcl_DString srcBuffer;
519
    Tcl_DString dstBuffer;
520
 
521
    Tcl_DStringInit(&srcBuffer);
522
    Tcl_DStringInit(&dstBuffer);
523
    Tcl_DStringAppend(&srcBuffer, src, -1);
524
    Tcl_DStringAppend(&dstBuffer, dst, -1);
525
    result = TraverseUnixTree(TraversalCopy, &srcBuffer, &dstBuffer,
526
            errorPtr);
527
    Tcl_DStringFree(&srcBuffer);
528
    Tcl_DStringFree(&dstBuffer);
529
    return result;
530
}
531
 
532
/*
533
 *---------------------------------------------------------------------------
534
 *
535
 * TclpRemoveDirectory --
536
 *
537
 *      Removes directory (and its contents, if the recursive flag is set).
538
 *
539
 * Results:
540
 *      If the directory was successfully removed, returns TCL_OK.
541
 *      Otherwise the return value is TCL_ERROR, errno is set to indicate
542
 *      the error, and the pathname of the file that caused the error
543
 *      is stored in errorPtr.  Some possible values for errno are:
544
 *
545
 *      EACCES:     path directory can't be read and/or written.
546
 *      EEXIST:     path is a non-empty directory.
547
 *      EINVAL:     path is a root directory.
548
 *      ENOENT:     path doesn't exist or is "".
549
 *      ENOTDIR:    path is not a directory.
550
 *
551
 * Side effects:
552
 *      Directory removed.  If an error occurs, the error will be returned
553
 *      immediately, and remaining files will not be deleted.
554
 *
555
 *---------------------------------------------------------------------------
556
 */
557
 
558
int
559
TclpRemoveDirectory(path, recursive, errorPtr)
560
    char *path;                 /* Pathname of directory to be removed. */
561
    int recursive;              /* If non-zero, removes directories that
562
                                 * are nonempty.  Otherwise, will only remove
563
                                 * empty directories. */
564
    Tcl_DString *errorPtr;      /* If non-NULL, initialized DString for
565
                                 * error reporting. */
566
{
567
    int result;
568
    Tcl_DString buffer;
569
 
570
    if (rmdir(path) == 0) {
571
        return TCL_OK;
572
    }
573
    if (errno == ENOTEMPTY) {
574
        errno = EEXIST;
575
    }
576
    if ((errno != EEXIST) || (recursive == 0)) {
577
        if (errorPtr != NULL) {
578
            Tcl_DStringAppend(errorPtr, path, -1);
579
        }
580
        return TCL_ERROR;
581
    }
582
 
583
    /*
584
     * The directory is nonempty, but the recursive flag has been
585
     * specified, so we recursively remove all the files in the directory.
586
     */
587
 
588
    Tcl_DStringInit(&buffer);
589
    Tcl_DStringAppend(&buffer, path, -1);
590
    result = TraverseUnixTree(TraversalDelete, &buffer, NULL, errorPtr);
591
    Tcl_DStringFree(&buffer);
592
    return result;
593
}
594
 
595
/*
596
 *---------------------------------------------------------------------------
597
 *
598
 * TraverseUnixTree --
599
 *
600
 *      Traverse directory tree specified by sourcePtr, calling the function
601
 *      traverseProc for each file and directory encountered.  If destPtr
602
 *      is non-null, each of name in the sourcePtr directory is appended to
603
 *      the directory specified by destPtr and passed as the second argument
604
 *      to traverseProc() .
605
 *
606
 * Results:
607
 *      Standard Tcl result.
608
 *
609
 * Side effects:
610
 *      None caused by TraverseUnixTree, however the user specified
611
 *      traverseProc() may change state.  If an error occurs, the error will
612
 *      be returned immediately, and remaining files will not be processed.
613
 *
614
 *---------------------------------------------------------------------------
615
 */
616
 
617
static int
618
TraverseUnixTree(traverseProc, sourcePtr, targetPtr, errorPtr)
619
    TraversalProc *traverseProc;/* Function to call for every file and
620
                                 * directory in source hierarchy. */
621
    Tcl_DString *sourcePtr;     /* Pathname of source directory to be
622
                                 * traversed. */
623
    Tcl_DString *targetPtr;     /* Pathname of directory to traverse in
624
                                 * parallel with source directory. */
625
    Tcl_DString *errorPtr;      /* If non-NULL, an initialized DString for
626
                                 * error reporting. */
627
{
628
    struct stat statbuf;
629
    char *source, *target, *errfile;
630
    int result, sourceLen;
631
    int targetLen = 0;           /* Initialization needed only to prevent
632
                                 * warning in gcc. */
633
    struct dirent *dirp;
634
    DIR *dp;
635
 
636
    result = TCL_OK;
637
    source = Tcl_DStringValue(sourcePtr);
638
    if (targetPtr != NULL) {
639
        target = Tcl_DStringValue(targetPtr);
640
    } else {
641
        target = NULL;
642
    }
643
 
644
    errfile = NULL;
645
    if (lstat(source, &statbuf) != 0) {
646
        errfile = source;
647
        goto end;
648
    }
649
    if (!S_ISDIR(statbuf.st_mode)) {
650
        /*
651
         * Process the regular file
652
         */
653
 
654
        return (*traverseProc)(source, target, &statbuf, DOTREE_F, errorPtr);
655
    }
656
 
657
    dp = opendir(source);
658
    if (dp == NULL) {
659
        /*
660
         * Can't read directory
661
         */
662
 
663
        errfile = source;
664
        goto end;
665
    }
666
    result = (*traverseProc)(source, target, &statbuf, DOTREE_PRED, errorPtr);
667
    if (result != TCL_OK) {
668
        closedir(dp);
669
        return result;
670
    }
671
 
672
    Tcl_DStringAppend(sourcePtr, "/", 1);
673
    source = Tcl_DStringValue(sourcePtr);
674
    sourceLen = Tcl_DStringLength(sourcePtr);
675
 
676
    if (targetPtr != NULL) {
677
        Tcl_DStringAppend(targetPtr, "/", 1);
678
        target = Tcl_DStringValue(targetPtr);
679
        targetLen = Tcl_DStringLength(targetPtr);
680
    }
681
 
682
    while ((dirp = readdir(dp)) != NULL) {
683
        if ((strcmp(dirp->d_name, ".") == 0)
684
                || (strcmp(dirp->d_name, "..") == 0)) {
685
            continue;
686
        }
687
 
688
        /*
689
         * Append name after slash, and recurse on the file.
690
         */
691
 
692
        Tcl_DStringAppend(sourcePtr, dirp->d_name, -1);
693
        if (targetPtr != NULL) {
694
            Tcl_DStringAppend(targetPtr, dirp->d_name, -1);
695
        }
696
        result = TraverseUnixTree(traverseProc, sourcePtr, targetPtr,
697
                errorPtr);
698
        if (result != TCL_OK) {
699
            break;
700
        }
701
 
702
        /*
703
         * Remove name after slash.
704
         */
705
 
706
        Tcl_DStringSetLength(sourcePtr, sourceLen);
707
        if (targetPtr != NULL) {
708
            Tcl_DStringSetLength(targetPtr, targetLen);
709
        }
710
    }
711
    closedir(dp);
712
 
713
    /*
714
     * Strip off the trailing slash we added
715
     */
716
 
717
    Tcl_DStringSetLength(sourcePtr, sourceLen - 1);
718
    source = Tcl_DStringValue(sourcePtr);
719
    if (targetPtr != NULL) {
720
        Tcl_DStringSetLength(targetPtr, targetLen - 1);
721
        target = Tcl_DStringValue(targetPtr);
722
    }
723
 
724
    if (result == TCL_OK) {
725
        /*
726
         * Call traverseProc() on a directory after visiting all the
727
         * files in that directory.
728
         */
729
 
730
        result = (*traverseProc)(source, target, &statbuf, DOTREE_POSTD,
731
                errorPtr);
732
    }
733
    end:
734
    if (errfile != NULL) {
735
        if (errorPtr != NULL) {
736
            Tcl_DStringAppend(errorPtr, errfile, -1);
737
        }
738
        result = TCL_ERROR;
739
    }
740
 
741
    return result;
742
}
743
 
744
/*
745
 *----------------------------------------------------------------------
746
 *
747
 * TraversalCopy
748
 *
749
 *      Called from TraverseUnixTree in order to execute a recursive copy of a
750
 *      directory.
751
 *
752
 * Results:
753
 *      Standard Tcl result.
754
 *
755
 * Side effects:
756
 *      The file or directory src may be copied to dst, depending on
757
 *      the value of type.
758
 *
759
 *----------------------------------------------------------------------
760
 */
761
 
762
static int
763
TraversalCopy(src, dst, sbPtr, type, errorPtr)
764
    char *src;                  /* Source pathname to copy. */
765
    char *dst;                  /* Destination pathname of copy. */
766
    struct stat *sbPtr;         /* Stat info for file specified by src. */
767
    int type;                   /* Reason for call - see TraverseUnixTree(). */
768
    Tcl_DString *errorPtr;      /* If non-NULL, initialized DString for
769
                                 * error return. */
770
{
771
    switch (type) {
772
        case DOTREE_F:
773
            if (TclpCopyFile(src, dst) == TCL_OK) {
774
                return TCL_OK;
775
            }
776
            break;
777
 
778
        case DOTREE_PRED:
779
            if (TclpCreateDirectory(dst) == TCL_OK) {
780
                return TCL_OK;
781
            }
782
            break;
783
 
784
        case DOTREE_POSTD:
785
            if (CopyFileAtts(src, dst, sbPtr) == TCL_OK) {
786
                return TCL_OK;
787
            }
788
            break;
789
 
790
    }
791
 
792
    /*
793
     * There shouldn't be a problem with src, because we already
794
     * checked it to get here.
795
     */
796
 
797
    if (errorPtr != NULL) {
798
        Tcl_DStringAppend(errorPtr, dst, -1);
799
    }
800
    return TCL_ERROR;
801
}
802
 
803
/*
804
 *---------------------------------------------------------------------------
805
 *
806
 * TraversalDelete --
807
 *
808
 *      Called by procedure TraverseUnixTree for every file and directory
809
 *      that it encounters in a directory hierarchy. This procedure unlinks
810
 *      files, and removes directories after all the containing files
811
 *      have been processed.
812
 *
813
 * Results:
814
 *      Standard Tcl result.
815
 *
816
 * Side effects:
817
 *      Files or directory specified by src will be deleted.
818
 *
819
 *----------------------------------------------------------------------
820
 */
821
 
822
static int
823
TraversalDelete(src, ignore, sbPtr, type, errorPtr)
824
    char *src;                  /* Source pathname. */
825
    char *ignore;               /* Destination pathname (not used). */
826
    struct stat *sbPtr;         /* Stat info for file specified by src. */
827
    int type;                   /* Reason for call - see TraverseUnixTree(). */
828
    Tcl_DString *errorPtr;      /* If non-NULL, initialized DString for
829
                                 * error return. */
830
{
831
    switch (type) {
832
        case DOTREE_F:
833
            if (unlink(src) == 0) {
834
                return TCL_OK;
835
            }
836
            break;
837
 
838
        case DOTREE_PRED:
839
            return TCL_OK;
840
 
841
        case DOTREE_POSTD:
842
            if (rmdir(src) == 0) {
843
                return TCL_OK;
844
            }
845
            break;
846
 
847
    }
848
 
849
    if (errorPtr != NULL) {
850
        Tcl_DStringAppend(errorPtr, src, -1);
851
    }
852
    return TCL_ERROR;
853
}
854
 
855
/*
856
 *----------------------------------------------------------------------
857
 *
858
 * CopyFileAtts
859
 *
860
 *      Copy the file attributes such as owner, group, permissions, and
861
 *      modification date from one file to another.
862
 *
863
 * Results:
864
 *      Standard Tcl result.
865
 *
866
 * Side effects:
867
 *      user id, group id, permission bits, last modification time, and
868
 *      last access time are updated in the new file to reflect the old
869
 *      file.
870
 *
871
 *----------------------------------------------------------------------
872
 */
873
 
874
static int
875
CopyFileAtts(src, dst, statBufPtr)
876
    char *src;                 /* Path name of source file */
877
    char *dst;                 /* Path name of target file */
878
    struct stat *statBufPtr;   /* ptr to stat info for source file */
879
{
880
    struct utimbuf tval;
881
    mode_t newMode;
882
 
883
    newMode = statBufPtr->st_mode
884
            & (S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO);
885
 
886
    /*
887
     * Note that if you copy a setuid file that is owned by someone
888
     * else, and you are not root, then the copy will be setuid to you.
889
     * The most correct implementation would probably be to have the
890
     * copy not setuid to anyone if the original file was owned by
891
     * someone else, but this corner case isn't currently handled.
892
     * It would require another lstat(), or getuid().
893
     */
894
 
895
    if (chmod(dst, newMode)) {
896
        newMode &= ~(S_ISUID | S_ISGID);
897
        if (chmod(dst, newMode)) {
898
            return TCL_ERROR;
899
        }
900
    }
901
 
902
    tval.actime = statBufPtr->st_atime;
903
    tval.modtime = statBufPtr->st_mtime;
904
 
905
    if (utime(dst, &tval)) {
906
        return TCL_ERROR;
907
    }
908
    return TCL_OK;
909
}
910
 
911
/*
912
 *----------------------------------------------------------------------
913
 *
914
 * GetGroupAttribute
915
 *
916
 *      Gets the group attribute of a file.
917
 *
918
 * Results:
919
 *      Standard TCL result. Returns a new Tcl_Obj in attributePtrPtr
920
 *      if there is no error.
921
 *
922
 * Side effects:
923
 *      A new object is allocated.
924
 *
925
 *----------------------------------------------------------------------
926
 */
927
 
928
static int
929
GetGroupAttribute(interp, objIndex, fileName, attributePtrPtr)
930
    Tcl_Interp *interp;         /* The interp we are using for errors. */
931
    int objIndex;               /* The index of the attribute. */
932
    char *fileName;             /* The name of the file. */
933
    Tcl_Obj **attributePtrPtr;  /* A pointer to return the object with. */
934
{
935
    struct stat statBuf;
936
    struct group *groupPtr;
937
 
938
    if (TclStat(fileName, &statBuf) != 0) {
939
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
940
                "could not stat file \"", fileName, "\": ",
941
                Tcl_PosixError(interp), (char *) NULL);
942
        return TCL_ERROR;
943
    }
944
 
945
    groupPtr = getgrgid(statBuf.st_gid);
946
    if (groupPtr == NULL) {
947
        *attributePtrPtr = Tcl_NewIntObj(statBuf.st_gid);
948
    } else {
949
        *attributePtrPtr = Tcl_NewStringObj(groupPtr->gr_name, -1);
950
    }
951
    endgrent();
952
    return TCL_OK;
953
}
954
 
955
/*
956
 *----------------------------------------------------------------------
957
 *
958
 * GetOwnerAttribute
959
 *
960
 *      Gets the owner attribute of a file.
961
 *
962
 * Results:
963
 *      Standard TCL result. Returns a new Tcl_Obj in attributePtrPtr
964
 *      if there is no error.
965
 *
966
 * Side effects:
967
 *      A new object is allocated.
968
 *
969
 *----------------------------------------------------------------------
970
 */
971
 
972
static int
973
GetOwnerAttribute(interp, objIndex, fileName, attributePtrPtr)
974
    Tcl_Interp *interp;         /* The interp we are using for errors. */
975
    int objIndex;               /* The index of the attribute. */
976
    char *fileName;             /* The name of the file. */
977
    Tcl_Obj **attributePtrPtr;  /* A pointer to return the object with. */
978
{
979
    struct stat statBuf;
980
    struct passwd *pwPtr;
981
 
982
    if (TclStat(fileName, &statBuf) != 0) {
983
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
984
                "could not stat file \"", fileName, "\": ",
985
                Tcl_PosixError(interp), (char *) NULL);
986
        return TCL_ERROR;
987
    }
988
 
989
    pwPtr = getpwuid(statBuf.st_uid);
990
    if (pwPtr == NULL) {
991
        *attributePtrPtr = Tcl_NewIntObj(statBuf.st_uid);
992
    } else {
993
        *attributePtrPtr = Tcl_NewStringObj(pwPtr->pw_name, -1);
994
    }
995
    endpwent();
996
    return TCL_OK;
997
}
998
 
999
/*
1000
 *----------------------------------------------------------------------
1001
 *
1002
 * GetPermissionsAttribute
1003
 *
1004
 *      Gets the group attribute of a file.
1005
 *
1006
 * Results:
1007
 *      Standard TCL result. Returns a new Tcl_Obj in attributePtrPtr
1008
 *      if there is no error. The object will have ref count 0.
1009
 *
1010
 * Side effects:
1011
 *      A new object is allocated.
1012
 *
1013
 *----------------------------------------------------------------------
1014
 */
1015
 
1016
static int
1017
GetPermissionsAttribute(interp, objIndex, fileName, attributePtrPtr)
1018
    Tcl_Interp *interp;             /* The interp we are using for errors. */
1019
    int objIndex;                   /* The index of the attribute. */
1020
    char *fileName;                 /* The name of the file. */
1021
    Tcl_Obj **attributePtrPtr;      /* A pointer to return the object with. */
1022
{
1023
    struct stat statBuf;
1024
    char returnString[6];
1025
 
1026
    if (TclStat(fileName, &statBuf) != 0) {
1027
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1028
                "could not stat file \"", fileName, "\": ",
1029
                Tcl_PosixError(interp), (char *) NULL);
1030
        return TCL_ERROR;
1031
    }
1032
 
1033
    sprintf(returnString, "%0#5lo", (statBuf.st_mode & 0x00007FFF));
1034
 
1035
    *attributePtrPtr = Tcl_NewStringObj(returnString, -1);
1036
 
1037
    return TCL_OK;
1038
}
1039
 
1040
/*
1041
 *----------------------------------------------------------------------
1042
 *
1043
 * SetGroupAttribute
1044
 *
1045
 *      Sets the file to the given group.
1046
 *
1047
 * Results:
1048
 *      Standard TCL result.
1049
 *
1050
 * Side effects:
1051
 *      The group of the file is changed.
1052
 *
1053
 *----------------------------------------------------------------------
1054
 */
1055
 
1056
static int
1057
SetGroupAttribute(interp, objIndex, fileName, attributePtr)
1058
    Tcl_Interp *interp;             /* The interp we are using for errors. */
1059
    int objIndex;                   /* The index of the attribute. */
1060
    char *fileName;                 /* The name of the file. */
1061
    Tcl_Obj *attributePtr;          /* The attribute to set. */
1062
{
1063
    gid_t groupNumber;
1064
    long placeHolder;
1065
 
1066
    if (Tcl_GetLongFromObj(interp, attributePtr, &placeHolder) != TCL_OK) {
1067
        struct group *groupPtr;
1068
        char *groupString = Tcl_GetStringFromObj(attributePtr, NULL);
1069
 
1070
        Tcl_ResetResult(interp);
1071
        groupPtr = getgrnam(groupString);
1072
        if (groupPtr == NULL) {
1073
            endgrent();
1074
            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1075
                    "could not set group for file \"", fileName,
1076
                    "\": group \"", groupString, "\" does not exist",
1077
                    (char *) NULL);
1078
            return TCL_ERROR;
1079
        }
1080
        groupNumber = groupPtr->gr_gid;
1081
    } else {
1082
        groupNumber = (gid_t) placeHolder;
1083
    }
1084
 
1085
    if (chown(fileName, -1, groupNumber) != 0) {
1086
        endgrent();
1087
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1088
                "could not set group for file \"", fileName, "\": ",
1089
                Tcl_PosixError(interp), (char *) NULL);
1090
        return TCL_ERROR;
1091
    }
1092
    endgrent();
1093
    return TCL_OK;
1094
}
1095
 
1096
/*
1097
 *----------------------------------------------------------------------
1098
 *
1099
 * SetOwnerAttribute
1100
 *
1101
 *      Sets the file to the given owner.
1102
 *
1103
 * Results:
1104
 *      Standard TCL result.
1105
 *
1106
 * Side effects:
1107
 *      The group of the file is changed.
1108
 *
1109
 *----------------------------------------------------------------------
1110
 */
1111
 
1112
static int
1113
SetOwnerAttribute(interp, objIndex, fileName, attributePtr)
1114
    Tcl_Interp *interp;             /* The interp we are using for errors. */
1115
    int objIndex;                   /* The index of the attribute. */
1116
    char *fileName;                 /* The name of the file. */
1117
    Tcl_Obj *attributePtr;          /* The attribute to set. */
1118
{
1119
    uid_t userNumber;
1120
    long placeHolder;
1121
 
1122
    if (Tcl_GetLongFromObj(interp, attributePtr, &placeHolder) != TCL_OK) {
1123
        struct passwd *pwPtr;
1124
        char *ownerString = Tcl_GetStringFromObj(attributePtr, NULL);
1125
 
1126
        Tcl_ResetResult(interp);
1127
        pwPtr = getpwnam(ownerString);
1128
        if (pwPtr == NULL) {
1129
            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1130
                    "could not set owner for file \"", fileName,
1131
                    "\": user \"", ownerString, "\" does not exist",
1132
                    (char *) NULL);
1133
            return TCL_ERROR;
1134
        }
1135
        userNumber = pwPtr->pw_uid;
1136
    } else {
1137
        userNumber = (uid_t) placeHolder;
1138
    }
1139
 
1140
    if (chown(fileName, userNumber, -1) != 0) {
1141
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1142
                "could not set owner for file \"", fileName, "\": ",
1143
                Tcl_PosixError(interp), (char *) NULL);
1144
        return TCL_ERROR;
1145
    }
1146
 
1147
    return TCL_OK;
1148
}
1149
 
1150
/*
1151
 *----------------------------------------------------------------------
1152
 *
1153
 * SetPermissionsAttribute
1154
 *
1155
 *      Sets the file to the given group.
1156
 *
1157
 * Results:
1158
 *      Standard TCL result.
1159
 *
1160
 * Side effects:
1161
 *      The group of the file is changed.
1162
 *
1163
 *----------------------------------------------------------------------
1164
 */
1165
 
1166
static int
1167
SetPermissionsAttribute(interp, objIndex, fileName, attributePtr)
1168
    Tcl_Interp *interp;             /* The interp we are using for errors. */
1169
    int objIndex;                   /* The index of the attribute. */
1170
    char *fileName;                 /* The name of the file. */
1171
    Tcl_Obj *attributePtr;          /* The attribute to set. */
1172
{
1173
    long modeInt;
1174
    mode_t newMode;
1175
 
1176
    /*
1177
     * mode_t is a long under SPARC; an int under SunOS. Since we do not
1178
     * know how big it really is, we get the long and then cast it
1179
     * down to a mode_t.
1180
     */
1181
 
1182
    if (Tcl_GetLongFromObj(interp, attributePtr, &modeInt)
1183
            != TCL_OK) {
1184
        return TCL_ERROR;
1185
    }
1186
 
1187
    newMode = (mode_t) modeInt;
1188
 
1189
    if (chmod(fileName, newMode) != 0) {
1190
        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1191
                "could not set permissions for file \"", fileName, "\": ",
1192
                Tcl_PosixError(interp), (char *) NULL);
1193
        return TCL_ERROR;
1194
    }
1195
    return TCL_OK;
1196
}
1197
/*
1198
 *---------------------------------------------------------------------------
1199
 *
1200
 * TclpListVolumes --
1201
 *
1202
 *      Lists the currently mounted volumes, which on UNIX is just /.
1203
 *
1204
 * Results:
1205
 *      A standard Tcl result.  Will always be TCL_OK, since there is no way
1206
 *      that this command can fail.  Also, the interpreter's result is set to
1207
 *      the list of volumes.
1208
 *
1209
 * Side effects:
1210
 *      None.
1211
 *
1212
 *---------------------------------------------------------------------------
1213
 */
1214
 
1215
int
1216
TclpListVolumes(interp)
1217
    Tcl_Interp *interp;                 /* Interpreter to which to pass
1218
                                         * the volume list. */
1219
{
1220
    Tcl_Obj *resultPtr;
1221
 
1222
    resultPtr = Tcl_GetObjResult(interp);
1223
    Tcl_SetStringObj(resultPtr, "/", 1);
1224
    return TCL_OK;
1225
}
1226
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.