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

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [tcl/] [win/] [tclWinFCmd.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
2
 * tclWinFCmd.c
3
 *
4
 *      This file implements the Windows specific portion of file manipulation
5
 *      subcommands of the "file" command.
6
 *
7
 * Copyright (c) 1996-1997 Sun Microsystems, Inc.
8
 *
9
 * See the file "license.terms" for information on usage and redistribution
10
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11
 *
12
 * RCS: @(#) $Id: tclWinFCmd.c,v 1.1.1.1 2002-01-16 10:25:38 markom Exp $
13
 */
14
 
15
#include "tclWinInt.h"
16
 
17
/*
18
 * The following constants specify the type of callback when
19
 * TraverseWinTree() calls the traverseProc()
20
 */
21
 
22
#define DOTREE_PRED   1     /* pre-order directory  */
23
#define DOTREE_POSTD  2     /* post-order directory */
24
#define DOTREE_F      3     /* regular file */
25
 
26
/*
27
 * Callbacks for file attributes code.
28
 */
29
 
30
static int              GetWinFileAttributes _ANSI_ARGS_((Tcl_Interp *interp,
31
                            int objIndex, char *fileName,
32
                            Tcl_Obj **attributePtrPtr));
33
static int              GetWinFileLongName _ANSI_ARGS_((Tcl_Interp *interp,
34
                            int objIndex, char *fileName,
35
                            Tcl_Obj **attributePtrPtr));
36
static int              GetWinFileShortName _ANSI_ARGS_((Tcl_Interp *interp,
37
                            int objIndex, char *fileName,
38
                            Tcl_Obj **attributePtrPtr));
39
static int              SetWinFileAttributes _ANSI_ARGS_((Tcl_Interp *interp,
40
                            int objIndex, char *fileName,
41
                            Tcl_Obj *attributePtr));
42
static int              CannotSetAttribute _ANSI_ARGS_((Tcl_Interp *interp,
43
                            int objIndex, char *fileName,
44
                            Tcl_Obj *attributePtr));
45
 
46
/*
47
 * Constants and variables necessary for file attributes subcommand.
48
 */
49
 
50
enum {
51
    WIN_ARCHIVE_ATTRIBUTE,
52
    WIN_HIDDEN_ATTRIBUTE,
53
    WIN_LONGNAME_ATTRIBUTE,
54
    WIN_READONLY_ATTRIBUTE,
55
    WIN_SHORTNAME_ATTRIBUTE,
56
    WIN_SYSTEM_ATTRIBUTE
57
};
58
 
59
static int attributeArray[] = {FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_HIDDEN,
60
        0, FILE_ATTRIBUTE_READONLY, 0, FILE_ATTRIBUTE_SYSTEM};
61
 
62
 
63
char *tclpFileAttrStrings[] = {"-archive", "-hidden", "-longname", "-readonly",
64
        "-shortname", "-system", (char *) NULL};
65
CONST TclFileAttrProcs tclpFileAttrProcs[] = {
66
        {GetWinFileAttributes, SetWinFileAttributes},
67
        {GetWinFileAttributes, SetWinFileAttributes},
68
        {GetWinFileLongName, CannotSetAttribute},
69
        {GetWinFileAttributes, SetWinFileAttributes},
70
        {GetWinFileShortName, CannotSetAttribute},
71
        {GetWinFileAttributes, SetWinFileAttributes}};
72
 
73
/*
74
 * Prototype for the TraverseWinTree callback function.
75
 */
76
 
77
typedef int (TraversalProc)(char *src, char *dst, DWORD attr, int type,
78
        Tcl_DString *errorPtr);
79
 
80
/*
81
 * Declarations for local procedures defined in this file:
82
 */
83
 
84
static void             AttributesPosixError _ANSI_ARGS_((Tcl_Interp *interp,
85
                            int objIndex, char *fileName, int getOrSet));
86
static int              ConvertFileNameFormat _ANSI_ARGS_((Tcl_Interp *interp,
87
                            int objIndex, char *fileName, int longShort,
88
                            Tcl_Obj **attributePtrPtr));
89
static int              TraversalCopy(char *src, char *dst, DWORD attr,
90
                                int type, Tcl_DString *errorPtr);
91
static int              TraversalDelete(char *src, char *dst, DWORD attr,
92
                                int type, Tcl_DString *errorPtr);
93
static int              TraverseWinTree(TraversalProc *traverseProc,
94
                            Tcl_DString *sourcePtr, Tcl_DString *destPtr,
95
                            Tcl_DString *errorPtr);
96
 
97
 
98
/*
99
 *---------------------------------------------------------------------------
100
 *
101
 * TclpRenameFile --
102
 *
103
 *      Changes the name of an existing file or directory, from src to dst.
104
 *      If src and dst refer to the same file or directory, does nothing
105
 *      and returns success.  Otherwise if dst already exists, it will be
106
 *      deleted and replaced by src subject to the following conditions:
107
 *          If src is a directory, dst may be an empty directory.
108
 *          If src is a file, dst may be a file.
109
 *      In any other situation where dst already exists, the rename will
110
 *      fail.
111
 *
112
 * Results:
113
 *      If the directory was successfully created, returns TCL_OK.
114
 *      Otherwise the return value is TCL_ERROR and errno is set to
115
 *      indicate the error.  Some possible values for errno are:
116
 *
117
 *      EACCES:     src or dst parent directory can't be read and/or written.
118
 *      EEXIST:     dst is a non-empty directory.
119
 *      EINVAL:     src is a root directory or dst is a subdirectory of src.
120
 *      EISDIR:     dst is a directory, but src is not.
121
 *      ENOENT:     src doesn't exist.  src or dst is "".
122
 *      ENOTDIR:    src is a directory, but dst is not.
123
 *      EXDEV:      src and dst are on different filesystems.
124
 *
125
 *      EACCES:     exists an open file already referring to src or dst.
126
 *      EACCES:     src or dst specify the current working directory (NT).
127
 *      EACCES:     src specifies a char device (nul:, com1:, etc.)
128
 *      EEXIST:     dst specifies a char device (nul:, com1:, etc.) (NT)
129
 *      EACCES:     dst specifies a char device (nul:, com1:, etc.) (95)
130
 *
131
 * Side effects:
132
 *      The implementation supports cross-filesystem renames of files,
133
 *      but the caller should be prepared to emulate cross-filesystem
134
 *      renames of directories if errno is EXDEV.
135
 *
136
 *---------------------------------------------------------------------------
137
 */
138
 
139
int
140
TclpRenameFile(
141
    char *src,                  /* Pathname of file or dir to be renamed. */
142
    char *dst)                  /* New pathname for file or directory. */
143
{
144
    DWORD srcAttr, dstAttr;
145
 
146
    /*
147
     * Would throw an exception under NT if one of the arguments is a
148
     * char block device.
149
     */
150
 
151
    /* CYGNUS LOCAL */
152
#ifndef __GNUC__
153
    try {
154
#endif
155
    /* END CYGNUS LOCAL */
156
        if (MoveFile(src, dst) != FALSE) {
157
            return TCL_OK;
158
        }
159
    /* CYGNUS LOCAL */
160
#ifndef __GNUC__
161
    } except (-1) {}
162
#endif
163
    /* END CYGNUS LOCAL */
164
 
165
    TclWinConvertError(GetLastError());
166
 
167
    srcAttr = GetFileAttributes(src);
168
    dstAttr = GetFileAttributes(dst);
169
    if (srcAttr == (DWORD) -1) {
170
        srcAttr = 0;
171
    }
172
    if (dstAttr == (DWORD) -1) {
173
        dstAttr = 0;
174
    }
175
 
176
    if (errno == EBADF) {
177
        errno = EACCES;
178
        return TCL_ERROR;
179
    }
180
    if ((errno == EACCES) && (TclWinGetPlatformId() == VER_PLATFORM_WIN32s)) {
181
        if ((srcAttr != 0) && (dstAttr != 0)) {
182
            /*
183
             * Win32s reports trying to overwrite an existing file or directory
184
             * as EACCES.
185
             */
186
 
187
            errno = EEXIST;
188
        }
189
    }
190
    if (errno == EACCES) {
191
        decode:
192
        if (srcAttr & FILE_ATTRIBUTE_DIRECTORY) {
193
            char srcPath[MAX_PATH], dstPath[MAX_PATH];
194
            int srcArgc, dstArgc;
195
            char **srcArgv, **dstArgv;
196
            char *srcRest, *dstRest;
197
            int size;
198
 
199
            size = GetFullPathName(src, sizeof(srcPath), srcPath, &srcRest);
200
            if ((size == 0) || (size > sizeof(srcPath))) {
201
                return TCL_ERROR;
202
            }
203
            size = GetFullPathName(dst, sizeof(dstPath), dstPath, &dstRest);
204
            if ((size == 0) || (size > sizeof(dstPath))) {
205
                return TCL_ERROR;
206
            }
207
            if (srcRest == NULL) {
208
                srcRest = srcPath + strlen(srcPath);
209
            }
210
            if (strnicmp(srcPath, dstPath, srcRest - srcPath) == 0) {
211
                /*
212
                 * Trying to move a directory into itself.
213
                 */
214
 
215
                errno = EINVAL;
216
                return TCL_ERROR;
217
            }
218
            Tcl_SplitPath(srcPath, &srcArgc, &srcArgv);
219
            Tcl_SplitPath(dstPath, &dstArgc, &dstArgv);
220
            if (srcArgc == 1) {
221
                /*
222
                 * They are trying to move a root directory.  Whether
223
                 * or not it is across filesystems, this cannot be
224
                 * done.
225
                 */
226
 
227
                errno = EINVAL;
228
            } else if ((srcArgc > 0) && (dstArgc > 0) &&
229
                    (stricmp(srcArgv[0], dstArgv[0]) != 0)) {
230
                /*
231
                 * If src is a directory and dst filesystem != src
232
                 * filesystem, errno should be EXDEV.  It is very
233
                 * important to get this behavior, so that the caller
234
                 * can respond to a cross filesystem rename by
235
                 * simulating it with copy and delete.  The MoveFile
236
                 * system call already handles the case of moving a
237
                 * file between filesystems.
238
                 */
239
 
240
                errno = EXDEV;
241
            }
242
 
243
            ckfree((char *) srcArgv);
244
            ckfree((char *) dstArgv);
245
        }
246
 
247
        /*
248
         * Other types of access failure is that dst is a read-only
249
         * filesystem, that an open file referred to src or dest, or that
250
         * src or dest specified the current working directory on the
251
         * current filesystem.  EACCES is returned for those cases.
252
         */
253
 
254
    } else if (errno == EEXIST) {
255
        /*
256
         * Reports EEXIST any time the target already exists.  If it makes
257
         * sense, remove the old file and try renaming again.
258
         */
259
 
260
        if (srcAttr & FILE_ATTRIBUTE_DIRECTORY) {
261
            if (dstAttr & FILE_ATTRIBUTE_DIRECTORY) {
262
                /*
263
                 * Overwrite empty dst directory with src directory.  The
264
                 * following call will remove an empty directory.  If it
265
                 * fails, it's because it wasn't empty.
266
                 */
267
 
268
                if (TclpRemoveDirectory(dst, 0, NULL) == TCL_OK) {
269
                    /*
270
                     * Now that that empty directory is gone, we can try
271
                     * renaming again.  If that fails, we'll put this empty
272
                     * directory back, for completeness.
273
                     */
274
 
275
                    if (MoveFile(src, dst) != FALSE) {
276
                        return TCL_OK;
277
                    }
278
 
279
                    /*
280
                     * Some new error has occurred.  Don't know what it
281
                     * could be, but report this one.
282
                     */
283
 
284
                    TclWinConvertError(GetLastError());
285
                    CreateDirectory(dst, NULL);
286
                    SetFileAttributes(dst, dstAttr);
287
                    if (errno == EACCES) {
288
                        /*
289
                         * Decode the EACCES to a more meaningful error.
290
                         */
291
 
292
                        goto decode;
293
                    }
294
                }
295
            } else {    /* (dstAttr & FILE_ATTRIBUTE_DIRECTORY) == 0 */
296
                errno = ENOTDIR;
297
            }
298
        } else {    /* (srcAttr & FILE_ATTRIBUTE_DIRECTORY) == 0 */
299
            if (dstAttr & FILE_ATTRIBUTE_DIRECTORY) {
300
                errno = EISDIR;
301
            } else {
302
                /*
303
                 * Overwrite existing file by:
304
                 *
305
                 * 1. Rename existing file to temp name.
306
                 * 2. Rename old file to new name.
307
                 * 3. If success, delete temp file.  If failure,
308
                 *    put temp file back to old name.
309
                 */
310
 
311
                char tempName[MAX_PATH];
312
                int result, size;
313
                char *rest;
314
 
315
                size = GetFullPathName(dst, sizeof(tempName), tempName, &rest);
316
                if ((size == 0) || (size > sizeof(tempName)) || (rest == NULL)) {
317
                    return TCL_ERROR;
318
                }
319
                *rest = '\0';
320
                result = TCL_ERROR;
321
                if (GetTempFileName(tempName, "tclr", 0, tempName) != 0) {
322
                    /*
323
                     * Strictly speaking, need the following DeleteFile and
324
                     * MoveFile to be joined as an atomic operation so no
325
                     * other app comes along in the meantime and creates the
326
                     * same temp file.
327
                     */
328
 
329
                    DeleteFile(tempName);
330
                    if (MoveFile(dst, tempName) != FALSE) {
331
                        if (MoveFile(src, dst) != FALSE) {
332
                            SetFileAttributes(tempName, FILE_ATTRIBUTE_NORMAL);
333
                            DeleteFile(tempName);
334
                            return TCL_OK;
335
                        } else {
336
                            DeleteFile(dst);
337
                            MoveFile(tempName, dst);
338
                        }
339
                    }
340
 
341
                    /*
342
                     * Can't backup dst file or move src file.  Return that
343
                     * error.  Could happen if an open file refers to dst.
344
                     */
345
 
346
                    TclWinConvertError(GetLastError());
347
                    if (errno == EACCES) {
348
                        /*
349
                         * Decode the EACCES to a more meaningful error.
350
                         */
351
 
352
                        goto decode;
353
                    }
354
                }
355
                return result;
356
            }
357
        }
358
    }
359
    return TCL_ERROR;
360
}
361
 
362
/*
363
 *---------------------------------------------------------------------------
364
 *
365
 * TclpCopyFile --
366
 *
367
 *      Copy a single file (not a directory).  If dst already exists and
368
 *      is not a directory, it is removed.
369
 *
370
 * Results:
371
 *      If the file was successfully copied, returns TCL_OK.  Otherwise
372
 *      the return value is TCL_ERROR and errno is set to indicate the
373
 *      error.  Some possible values for errno are:
374
 *
375
 *      EACCES:     src or dst parent directory can't be read and/or written.
376
 *      EISDIR:     src or dst is a directory.
377
 *      ENOENT:     src doesn't exist.  src or dst is "".
378
 *
379
 *      EACCES:     exists an open file already referring to dst (95).
380
 *      EACCES:     src specifies a char device (nul:, com1:, etc.) (NT)
381
 *      ENOENT:     src specifies a char device (nul:, com1:, etc.) (95)
382
 *
383
 * Side effects:
384
 *      It is not an error to copy to a char device.
385
 *
386
 *---------------------------------------------------------------------------
387
 */
388
 
389
int
390
TclpCopyFile(
391
    char *src,                  /* Pathname of file to be copied. */
392
    char *dst)                  /* Pathname of file to copy to. */
393
{
394
    /*
395
     * Would throw an exception under NT if one of the arguments is a char
396
     * block device.
397
     */
398
 
399
    /* CYGNUS LOCAL */
400
#ifndef __GNUC__
401
    try {
402
#endif /* __GNUC__ */
403
      if (CopyFile(src, dst, 0) != FALSE) {
404
            return TCL_OK;
405
        }
406
    /* CYGNUS LOCAL */
407
#ifndef __GNUC__
408
    } except (-1) {}
409
#endif /* __GNUC__ */
410
 
411
    TclWinConvertError(GetLastError());
412
    if (errno == EBADF) {
413
        errno = EACCES;
414
        return TCL_ERROR;
415
    }
416
    if (errno == EACCES) {
417
        DWORD srcAttr, dstAttr;
418
 
419
        srcAttr = GetFileAttributes(src);
420
        dstAttr = GetFileAttributes(dst);
421
        if (srcAttr != (DWORD) -1) {
422
            if (dstAttr == (DWORD) -1) {
423
                dstAttr = 0;
424
            }
425
            if ((srcAttr & FILE_ATTRIBUTE_DIRECTORY) ||
426
                    (dstAttr & FILE_ATTRIBUTE_DIRECTORY)) {
427
                errno = EISDIR;
428
            }
429
            if (dstAttr & FILE_ATTRIBUTE_READONLY) {
430
                SetFileAttributes(dst, dstAttr & ~FILE_ATTRIBUTE_READONLY);
431
                if (CopyFile(src, dst, 0) != FALSE) {
432
                    return TCL_OK;
433
                }
434
                /*
435
                 * Still can't copy onto dst.  Return that error, and
436
                 * restore attributes of dst.
437
                 */
438
 
439
                TclWinConvertError(GetLastError());
440
                SetFileAttributes(dst, dstAttr);
441
            }
442
        }
443
    }
444
    return TCL_ERROR;
445
}
446
 
447
/*
448
 *---------------------------------------------------------------------------
449
 *
450
 * TclpDeleteFile --
451
 *
452
 *      Removes a single file (not a directory).
453
 *
454
 * Results:
455
 *      If the file was successfully deleted, returns TCL_OK.  Otherwise
456
 *      the return value is TCL_ERROR and errno is set to indicate the
457
 *      error.  Some possible values for errno are:
458
 *
459
 *      EACCES:     a parent directory can't be read and/or written.
460
 *      EISDIR:     path is a directory.
461
 *      ENOENT:     path doesn't exist or is "".
462
 *
463
 *      EACCES:     exists an open file already referring to path.
464
 *      EACCES:     path is a char device (nul:, com1:, etc.)
465
 *
466
 * Side effects:
467
 *      The file is deleted, even if it is read-only.
468
 *
469
 *---------------------------------------------------------------------------
470
 */
471
 
472
int
473
TclpDeleteFile(
474
    char *path)                 /* Pathname of file to be removed. */
475
{
476
    DWORD attr;
477
 
478
    if (DeleteFile(path) != FALSE) {
479
        return TCL_OK;
480
    }
481
    TclWinConvertError(GetLastError());
482
    if (path[0] == '\0') {
483
        /*
484
         * Win32s thinks that "" is the same as "." and then reports EISDIR
485
         * instead of ENOENT.
486
         */
487
 
488
        errno = ENOENT;
489
    } else if (errno == EACCES) {
490
        attr = GetFileAttributes(path);
491
        if (attr != (DWORD) -1) {
492
            if (attr & FILE_ATTRIBUTE_DIRECTORY) {
493
                /*
494
                 * Windows NT reports removing a directory as EACCES instead
495
                 * of EISDIR.
496
                 */
497
 
498
                errno = EISDIR;
499
            } else if (attr & FILE_ATTRIBUTE_READONLY) {
500
                SetFileAttributes(path, attr & ~FILE_ATTRIBUTE_READONLY);
501
                if (DeleteFile(path) != FALSE) {
502
                    return TCL_OK;
503
                }
504
                TclWinConvertError(GetLastError());
505
                SetFileAttributes(path, attr);
506
            }
507
        }
508
    } else if (errno == ENOENT) {
509
        attr = GetFileAttributes(path);
510
        if (attr != (DWORD) -1) {
511
            if (attr & FILE_ATTRIBUTE_DIRECTORY) {
512
                /*
513
                 * Windows 95 reports removing a directory as ENOENT instead
514
                 * of EISDIR.
515
                 */
516
 
517
                errno = EISDIR;
518
            }
519
        }
520
    } else if (errno == EINVAL) {
521
        /*
522
         * Windows NT reports removing a char device as EINVAL instead of
523
         * EACCES.
524
         */
525
 
526
        errno = EACCES;
527
    }
528
 
529
    return TCL_ERROR;
530
}
531
 
532
/*
533
 *---------------------------------------------------------------------------
534
 *
535
 * TclpCreateDirectory --
536
 *
537
 *      Creates the specified directory.  All parent directories of the
538
 *      specified directory must already exist.  The directory is
539
 *      automatically created with permissions so that user can access
540
 *      the new directory and create new files or subdirectories in it.
541
 *
542
 * Results:
543
 *      If the directory was successfully created, returns TCL_OK.
544
 *      Otherwise the return value is TCL_ERROR and errno is set to
545
 *      indicate the error.  Some possible values for errno are:
546
 *
547
 *      EACCES:     a parent directory can't be read and/or written.
548
 *      EEXIST:     path already exists.
549
 *      ENOENT:     a parent directory doesn't exist.
550
 *
551
 * Side effects:
552
 *      A directory is created.
553
 *
554
 *---------------------------------------------------------------------------
555
 */
556
 
557
int
558
TclpCreateDirectory(
559
    char *path)                 /* Pathname of directory to create */
560
{
561
    int error;
562
 
563
    if (CreateDirectory(path, NULL) == 0) {
564
        error = GetLastError();
565
        if (TclWinGetPlatformId() == VER_PLATFORM_WIN32s) {
566
            if ((error == ERROR_ACCESS_DENIED)
567
                    && (GetFileAttributes(path) != (DWORD) -1)) {
568
                error = ERROR_FILE_EXISTS;
569
            }
570
        }
571
        TclWinConvertError(error);
572
        return TCL_ERROR;
573
    }
574
    return TCL_OK;
575
}
576
 
577
/*
578
 *---------------------------------------------------------------------------
579
 *
580
 * TclpCopyDirectory --
581
 *
582
 *      Recursively copies a directory.  The target directory dst must
583
 *      not already exist.  Note that this function does not merge two
584
 *      directory hierarchies, even if the target directory is an an
585
 *      empty directory.
586
 *
587
 * Results:
588
 *      If the directory was successfully copied, returns TCL_OK.
589
 *      Otherwise the return value is TCL_ERROR, errno is set to indicate
590
 *      the error, and the pathname of the file that caused the error
591
 *      is stored in errorPtr.  See TclpCreateDirectory and TclpCopyFile
592
 *      for a description of possible values for errno.
593
 *
594
 * Side effects:
595
 *      An exact copy of the directory hierarchy src will be created
596
 *      with the name dst.  If an error occurs, the error will
597
 *      be returned immediately, and remaining files will not be
598
 *      processed.
599
 *
600
 *---------------------------------------------------------------------------
601
 */
602
 
603
int
604
TclpCopyDirectory(
605
    char *src,                  /* Pathname of directory to be copied. */
606
    char *dst,                  /* Pathname of target directory. */
607
    Tcl_DString *errorPtr)      /* If non-NULL, initialized DString for
608
                                 * error reporting. */
609
{
610
    int result;
611
    Tcl_DString srcBuffer;
612
    Tcl_DString dstBuffer;
613
 
614
    Tcl_DStringInit(&srcBuffer);
615
    Tcl_DStringInit(&dstBuffer);
616
    Tcl_DStringAppend(&srcBuffer, src, -1);
617
    Tcl_DStringAppend(&dstBuffer, dst, -1);
618
    result = TraverseWinTree(TraversalCopy, &srcBuffer, &dstBuffer,
619
            errorPtr);
620
    Tcl_DStringFree(&srcBuffer);
621
    Tcl_DStringFree(&dstBuffer);
622
    return result;
623
}
624
 
625
/*
626
 *----------------------------------------------------------------------
627
 *
628
 * TclpRemoveDirectory --
629
 *
630
 *      Removes directory (and its contents, if the recursive flag is set).
631
 *
632
 * Results:
633
 *      If the directory was successfully removed, returns TCL_OK.
634
 *      Otherwise the return value is TCL_ERROR, errno is set to indicate
635
 *      the error, and the pathname of the file that caused the error
636
 *      is stored in errorPtr.  Some possible values for errno are:
637
 *
638
 *      EACCES:     path directory can't be read and/or written.
639
 *      EEXIST:     path is a non-empty directory.
640
 *      EINVAL:     path is root directory or current directory.
641
 *      ENOENT:     path doesn't exist or is "".
642
 *      ENOTDIR:    path is not a directory.
643
 *
644
 *      EACCES:     path is a char device (nul:, com1:, etc.) (95)
645
 *      EINVAL:     path is a char device (nul:, com1:, etc.) (NT)
646
 *
647
 * Side effects:
648
 *      Directory removed.  If an error occurs, the error will be returned
649
 *      immediately, and remaining files will not be deleted.
650
 *
651
 *----------------------------------------------------------------------
652
 */
653
 
654
int
655
TclpRemoveDirectory(
656
    char *path,                 /* Pathname of directory to be removed. */
657
    int recursive,              /* If non-zero, removes directories that
658
                                 * are nonempty.  Otherwise, will only remove
659
                                 * empty directories. */
660
    Tcl_DString *errorPtr)      /* If non-NULL, initialized DString for
661
                                 * error reporting. */
662
{
663
    int result;
664
    Tcl_DString buffer;
665
    DWORD attr;
666
 
667
    if (RemoveDirectory(path) != FALSE) {
668
        return TCL_OK;
669
    }
670
    TclWinConvertError(GetLastError());
671
    if (path[0] == '\0') {
672
        /*
673
         * Win32s thinks that "" is the same as "." and then reports EACCES
674
         * instead of ENOENT.
675
         */
676
 
677
        errno = ENOENT;
678
    }
679
    if (errno == EACCES) {
680
        attr = GetFileAttributes(path);
681
        if (attr != (DWORD) -1) {
682
            if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
683
                /*
684
                 * Windows 95 reports calling RemoveDirectory on a file as an
685
                 * EACCES, not an ENOTDIR.
686
                 */
687
 
688
                errno = ENOTDIR;
689
                goto end;
690
            }
691
 
692
            if (attr & FILE_ATTRIBUTE_READONLY) {
693
                attr &= ~FILE_ATTRIBUTE_READONLY;
694
                if (SetFileAttributes(path, attr) == FALSE) {
695
                    goto end;
696
                }
697
                if (RemoveDirectory(path) != FALSE) {
698
                    return TCL_OK;
699
                }
700
                TclWinConvertError(GetLastError());
701
                SetFileAttributes(path, attr | FILE_ATTRIBUTE_READONLY);
702
            }
703
 
704
            /*
705
             * Windows 95 and Win32s report removing a non-empty directory
706
             * as EACCES, not EEXIST.  If the directory is not empty,
707
             * change errno so caller knows what's going on.
708
             */
709
 
710
            if (TclWinGetPlatformId() != VER_PLATFORM_WIN32_NT) {
711
                HANDLE handle;
712
                WIN32_FIND_DATA data;
713
                Tcl_DString buffer;
714
                char *find;
715
                int len;
716
 
717
                Tcl_DStringInit(&buffer);
718
                find = Tcl_DStringAppend(&buffer, path, -1);
719
                len = Tcl_DStringLength(&buffer);
720
                if ((len > 0) && (find[len - 1] != '\\')) {
721
                    Tcl_DStringAppend(&buffer, "\\", 1);
722
                }
723
                find = Tcl_DStringAppend(&buffer, "*.*", 3);
724
                handle = FindFirstFile(find, &data);
725
                if (handle != INVALID_HANDLE_VALUE) {
726
                    while (1) {
727
                        if ((strcmp(data.cFileName, ".") != 0)
728
                                && (strcmp(data.cFileName, "..") != 0)) {
729
                            /*
730
                             * Found something in this directory.
731
                             */
732
 
733
                            errno = EEXIST;
734
                            break;
735
                        }
736
                        if (FindNextFile(handle, &data) == FALSE) {
737
                            break;
738
                        }
739
                    }
740
                    FindClose(handle);
741
                }
742
                Tcl_DStringFree(&buffer);
743
            }
744
        }
745
    }
746
    if (errno == ENOTEMPTY) {
747
        /*
748
         * The caller depends on EEXIST to signify that the directory is
749
         * not empty, not ENOTEMPTY.
750
         */
751
 
752
        errno = EEXIST;
753
    }
754
    if ((recursive != 0) && (errno == EEXIST)) {
755
        /*
756
         * The directory is nonempty, but the recursive flag has been
757
         * specified, so we recursively remove all the files in the directory.
758
         */
759
 
760
        Tcl_DStringInit(&buffer);
761
        Tcl_DStringAppend(&buffer, path, -1);
762
        result = TraverseWinTree(TraversalDelete, &buffer, NULL, errorPtr);
763
        Tcl_DStringFree(&buffer);
764
        return result;
765
    }
766
 
767
    end:
768
    if (errorPtr != NULL) {
769
        Tcl_DStringAppend(errorPtr, path, -1);
770
    }
771
    return TCL_ERROR;
772
}
773
 
774
/*
775
 *---------------------------------------------------------------------------
776
 *
777
 * TraverseWinTree --
778
 *
779
 *      Traverse directory tree specified by sourcePtr, calling the function
780
 *      traverseProc for each file and directory encountered.  If destPtr
781
 *      is non-null, each of name in the sourcePtr directory is appended to
782
 *      the directory specified by destPtr and passed as the second argument
783
 *      to traverseProc() .
784
 *
785
 * Results:
786
 *      Standard Tcl result.
787
 *
788
 * Side effects:
789
 *      None caused by TraverseWinTree, however the user specified
790
 *      traverseProc() may change state.  If an error occurs, the error will
791
 *      be returned immediately, and remaining files will not be processed.
792
 *
793
 *---------------------------------------------------------------------------
794
 */
795
 
796
static int
797
TraverseWinTree(
798
    TraversalProc *traverseProc,/* Function to call for every file and
799
                                 * directory in source hierarchy. */
800
    Tcl_DString *sourcePtr,     /* Pathname of source directory to be
801
                                 * traversed. */
802
    Tcl_DString *targetPtr,     /* Pathname of directory to traverse in
803
                                 * parallel with source directory. */
804
    Tcl_DString *errorPtr)      /* If non-NULL, an initialized DString for
805
                                 * error reporting. */
806
{
807
    DWORD sourceAttr;
808
    char *source, *target, *errfile;
809
    int result, sourceLen, targetLen, sourceLenOriginal, targetLenOriginal;
810
    HANDLE handle;
811
    WIN32_FIND_DATA data;
812
 
813
    result = TCL_OK;
814
    source = Tcl_DStringValue(sourcePtr);
815
    sourceLenOriginal = Tcl_DStringLength(sourcePtr);
816
    if (targetPtr != NULL) {
817
        target = Tcl_DStringValue(targetPtr);
818
        targetLenOriginal = Tcl_DStringLength(targetPtr);
819
    } else {
820
        target = NULL;
821
        targetLenOriginal = 0;
822
    }
823
 
824
    errfile = NULL;
825
 
826
    sourceAttr = GetFileAttributes(source);
827
    if (sourceAttr == (DWORD) -1) {
828
        errfile = source;
829
        goto end;
830
    }
831
    if ((sourceAttr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
832
        /*
833
         * Process the regular file
834
         */
835
 
836
        return (*traverseProc)(source, target, sourceAttr, DOTREE_F, errorPtr);
837
    }
838
 
839
    /*
840
     * When given the pathname of the form "c:\" (one that already ends
841
     * with a backslash), must make sure not to add another "\" to the end
842
     * otherwise it will try to access a network drive.
843
     */
844
 
845
    sourceLen = sourceLenOriginal;
846
    if ((sourceLen > 0) && (source[sourceLen - 1] != '\\')) {
847
        Tcl_DStringAppend(sourcePtr, "\\", 1);
848
        sourceLen++;
849
    }
850
    source = Tcl_DStringAppend(sourcePtr, "*.*", 3);
851
    handle = FindFirstFile(source, &data);
852
    Tcl_DStringSetLength(sourcePtr, sourceLen);
853
    if (handle == INVALID_HANDLE_VALUE) {
854
        /*
855
         * Can't read directory
856
         */
857
 
858
        TclWinConvertError(GetLastError());
859
        errfile = source;
860
        goto end;
861
    }
862
 
863
    result = (*traverseProc)(source, target, sourceAttr, DOTREE_PRED, errorPtr);
864
    if (result != TCL_OK) {
865
        FindClose(handle);
866
        return result;
867
    }
868
 
869
    if (targetPtr != NULL) {
870
        targetLen = targetLenOriginal;
871
        if ((targetLen > 0) && (target[targetLen - 1] != '\\')) {
872
            target = Tcl_DStringAppend(targetPtr, "\\", 1);
873
            targetLen++;
874
        }
875
    }
876
 
877
    while (1) {
878
        if ((strcmp(data.cFileName, ".") != 0)
879
                && (strcmp(data.cFileName, "..") != 0)) {
880
            /*
881
             * Append name after slash, and recurse on the file.
882
             */
883
 
884
            Tcl_DStringAppend(sourcePtr, data.cFileName, -1);
885
            if (targetPtr != NULL) {
886
                Tcl_DStringAppend(targetPtr, data.cFileName, -1);
887
            }
888
            result = TraverseWinTree(traverseProc, sourcePtr, targetPtr,
889
                    errorPtr);
890
            if (result != TCL_OK) {
891
                break;
892
            }
893
 
894
            /*
895
             * Remove name after slash.
896
             */
897
 
898
            Tcl_DStringSetLength(sourcePtr, sourceLen);
899
            if (targetPtr != NULL) {
900
                Tcl_DStringSetLength(targetPtr, targetLen);
901
            }
902
        }
903
        if (FindNextFile(handle, &data) == FALSE) {
904
            break;
905
        }
906
    }
907
    FindClose(handle);
908
 
909
    /*
910
     * Strip off the trailing slash we added
911
     */
912
 
913
    Tcl_DStringSetLength(sourcePtr, sourceLenOriginal);
914
    source = Tcl_DStringValue(sourcePtr);
915
    if (targetPtr != NULL) {
916
        Tcl_DStringSetLength(targetPtr, targetLenOriginal);
917
        target = Tcl_DStringValue(targetPtr);
918
    }
919
 
920
    if (result == TCL_OK) {
921
        /*
922
         * Call traverseProc() on a directory after visiting all the
923
         * files in that directory.
924
         */
925
 
926
        result = (*traverseProc)(source, target, sourceAttr,
927
                DOTREE_POSTD, errorPtr);
928
    }
929
    end:
930
    if (errfile != NULL) {
931
        TclWinConvertError(GetLastError());
932
        if (errorPtr != NULL) {
933
            Tcl_DStringAppend(errorPtr, errfile, -1);
934
        }
935
        result = TCL_ERROR;
936
    }
937
 
938
    return result;
939
}
940
 
941
/*
942
 *----------------------------------------------------------------------
943
 *
944
 * TraversalCopy
945
 *
946
 *      Called from TraverseUnixTree in order to execute a recursive
947
 *      copy of a directory.
948
 *
949
 * Results:
950
 *      Standard Tcl result.
951
 *
952
 * Side effects:
953
 *      Depending on the value of type, src may be copied to dst.
954
 *
955
 *----------------------------------------------------------------------
956
 */
957
 
958
static int
959
TraversalCopy(
960
    char *src,                  /* Source pathname to copy. */
961
    char *dst,                  /* Destination pathname of copy. */
962
    DWORD srcAttr,              /* File attributes for src. */
963
    int type,                   /* Reason for call - see TraverseWinTree() */
964
    Tcl_DString *errorPtr)      /* If non-NULL, initialized DString for
965
                                 * error return. */
966
{
967
    switch (type) {
968
        case DOTREE_F:
969
            if (TclpCopyFile(src, dst) == TCL_OK) {
970
                return TCL_OK;
971
            }
972
            break;
973
 
974
        case DOTREE_PRED:
975
            if (TclpCreateDirectory(dst) == TCL_OK) {
976
                if (SetFileAttributes(dst, srcAttr) != FALSE) {
977
                    return TCL_OK;
978
                }
979
                TclWinConvertError(GetLastError());
980
            }
981
            break;
982
 
983
        case DOTREE_POSTD:
984
            return TCL_OK;
985
 
986
    }
987
 
988
    /*
989
     * There shouldn't be a problem with src, because we already
990
     * checked it to get here.
991
     */
992
 
993
    if (errorPtr != NULL) {
994
        Tcl_DStringAppend(errorPtr, dst, -1);
995
    }
996
    return TCL_ERROR;
997
}
998
 
999
/*
1000
 *----------------------------------------------------------------------
1001
 *
1002
 * TraversalDelete --
1003
 *
1004
 *      Called by procedure TraverseWinTree for every file and
1005
 *      directory that it encounters in a directory hierarchy. This
1006
 *      procedure unlinks files, and removes directories after all the
1007
 *      containing files have been processed.
1008
 *
1009
 * Results:
1010
 *      Standard Tcl result.
1011
 *
1012
 * Side effects:
1013
 *      Files or directory specified by src will be deleted. If an
1014
 *      error occurs, the windows error is converted to a Posix error
1015
 *      and errno is set accordingly.
1016
 *
1017
 *----------------------------------------------------------------------
1018
 */
1019
 
1020
static int
1021
TraversalDelete(
1022
    char *src,                  /* Source pathname. */
1023
    char *ignore,               /* Destination pathname (not used). */
1024
    DWORD srcAttr,              /* File attributes for src (not used). */
1025
    int type,                   /* Reason for call - see TraverseWinTree(). */
1026
    Tcl_DString *errorPtr)      /* If non-NULL, initialized DString for
1027
                                 * error return. */
1028
{
1029
    switch (type) {
1030
        case DOTREE_F:
1031
            if (TclpDeleteFile(src) == TCL_OK) {
1032
                return TCL_OK;
1033
            }
1034
            break;
1035
 
1036
        case DOTREE_PRED:
1037
            return TCL_OK;
1038
 
1039
        case DOTREE_POSTD:
1040
            if (TclpRemoveDirectory(src, 0, NULL) == TCL_OK) {
1041
                return TCL_OK;
1042
            }
1043
            break;
1044
 
1045
    }
1046
 
1047
    if (errorPtr != NULL) {
1048
        Tcl_DStringAppend(errorPtr, src, -1);
1049
    }
1050
    return TCL_ERROR;
1051
}
1052
 
1053
/*
1054
 *----------------------------------------------------------------------
1055
 *
1056
 * AttributesPosixError --
1057
 *
1058
 *      Sets the object result with the appropriate error.
1059
 *
1060
 * Results:
1061
 *      None.
1062
 *
1063
 * Side effects:
1064
 *      The interp's object result is set with an error message
1065
 *      based on the objIndex, fileName and errno.
1066
 *
1067
 *----------------------------------------------------------------------
1068
 */
1069
 
1070
static void
1071
AttributesPosixError(
1072
    Tcl_Interp *interp,         /* The interp that has the error */
1073
    int objIndex,               /* The attribute which caused the problem. */
1074
    char *fileName,             /* The name of the file which caused the
1075
                                 * error. */
1076
    int getOrSet)               /* 0 for get; 1 for set */
1077
{
1078
    TclWinConvertError(GetLastError());
1079
    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1080
            "cannot ", getOrSet ? "set" : "get", " attribute \"",
1081
            tclpFileAttrStrings[objIndex], "\" for file \"", fileName,
1082
            "\": ", Tcl_PosixError(interp), (char *) NULL);
1083
}
1084
 
1085
/*
1086
 *----------------------------------------------------------------------
1087
 *
1088
 * GetWinFileAttributes --
1089
 *
1090
 *      Returns a Tcl_Obj containing the value of a file attribute.
1091
 *      This routine gets the -hidden, -readonly or -system attribute.
1092
 *
1093
 * Results:
1094
 *      Standard Tcl result and a Tcl_Obj in attributePtrPtr. The object
1095
 *      will have ref count 0. If the return value is not TCL_OK,
1096
 *      attributePtrPtr is not touched.
1097
 *
1098
 * Side effects:
1099
 *      A new object is allocated if the file is valid.
1100
 *
1101
 *----------------------------------------------------------------------
1102
 */
1103
 
1104
static int
1105
GetWinFileAttributes(
1106
    Tcl_Interp *interp,             /* The interp we are using for errors. */
1107
    int objIndex,                   /* The index of the attribute. */
1108
    char *fileName,                 /* The name of the file. */
1109
    Tcl_Obj **attributePtrPtr)      /* A pointer to return the object with. */
1110
{
1111
    DWORD result = GetFileAttributes(fileName);
1112
 
1113
    if (result == 0xFFFFFFFF) {
1114
        AttributesPosixError(interp, objIndex, fileName, 0);
1115
        return TCL_ERROR;
1116
    }
1117
 
1118
    *attributePtrPtr = Tcl_NewBooleanObj(result & attributeArray[objIndex]);
1119
    return TCL_OK;
1120
}
1121
 
1122
/*
1123
 *----------------------------------------------------------------------
1124
 *
1125
 * ConvertFileNameFormat --
1126
 *
1127
 *      Returns a Tcl_Obj containing either the long or short version of the
1128
 *      file name.
1129
 *
1130
 * Results:
1131
 *      Standard Tcl result and a Tcl_Obj in attributePtrPtr. The object
1132
 *      will have ref count 0. If the return value is not TCL_OK,
1133
 *      attributePtrPtr is not touched.
1134
 *
1135
 * Side effects:
1136
 *      A new object is allocated if the file is valid.
1137
 *
1138
 *----------------------------------------------------------------------
1139
 */
1140
 
1141
static int
1142
ConvertFileNameFormat(
1143
    Tcl_Interp *interp,             /* The interp we are using for errors. */
1144
    int objIndex,                   /* The index of the attribute. */
1145
    char *fileName,                 /* The name of the file. */
1146
    int longShort,                  /* 0 to short name, 1 to long name. */
1147
    Tcl_Obj **attributePtrPtr)      /* A pointer to return the object with. */
1148
{
1149
    HANDLE findHandle;
1150
    WIN32_FIND_DATA findData;
1151
    int pathArgc, i;
1152
    char **pathArgv, **newPathArgv;
1153
    char *currentElement, *resultStr;
1154
    Tcl_DString resultDString;
1155
    int result = TCL_OK;
1156
 
1157
    Tcl_SplitPath(fileName, &pathArgc, &pathArgv);
1158
    newPathArgv = (char **) ckalloc(pathArgc * sizeof(char *));
1159
 
1160
    i = 0;
1161
    if ((pathArgv[0][0] == '/')
1162
            || ((strlen(pathArgv[0]) == 3) && (pathArgv[0][1] == ':'))) {
1163
        newPathArgv[0] = (char *) ckalloc(strlen(pathArgv[0]) + 1);
1164
        strcpy(newPathArgv[0], pathArgv[0]);
1165
        i = 1;
1166
    }
1167
    for ( ; i < pathArgc; i++) {
1168
        if (strcmp(pathArgv[i], ".") == 0) {
1169
            currentElement = ckalloc(2);
1170
            strcpy(currentElement, ".");
1171
        } else if (strcmp(pathArgv[i], "..") == 0) {
1172
            currentElement = ckalloc(3);
1173
            strcpy(currentElement, "..");
1174
        } else {
1175
            int useLong;
1176
 
1177
            Tcl_DStringInit(&resultDString);
1178
            resultStr = Tcl_JoinPath(i + 1, pathArgv, &resultDString);
1179
            findHandle = FindFirstFile(resultStr, &findData);
1180
            if (findHandle == INVALID_HANDLE_VALUE) {
1181
                pathArgc = i - 1;
1182
                AttributesPosixError(interp, objIndex, fileName, 0);
1183
                result = TCL_ERROR;
1184
                Tcl_DStringFree(&resultDString);
1185
                goto cleanup;
1186
            }
1187
            if (longShort) {
1188
                if (findData.cFileName[0] != '\0') {
1189
                    useLong = 1;
1190
                } else {
1191
                    useLong = 0;
1192
                }
1193
            } else {
1194
                if (findData.cAlternateFileName[0] == '\0') {
1195
                    useLong = 1;
1196
                } else {
1197
                    useLong = 0;
1198
                }
1199
            }
1200
            if (useLong) {
1201
                currentElement = ckalloc(strlen(findData.cFileName) + 1);
1202
                strcpy(currentElement, findData.cFileName);
1203
            } else {
1204
                currentElement = ckalloc(strlen(findData.cAlternateFileName)
1205
                        + 1);
1206
                strcpy(currentElement, findData.cAlternateFileName);
1207
            }
1208
            Tcl_DStringFree(&resultDString);
1209
            FindClose(findHandle);
1210
        }
1211
        newPathArgv[i] = currentElement;
1212
    }
1213
 
1214
    Tcl_DStringInit(&resultDString);
1215
    resultStr = Tcl_JoinPath(pathArgc, newPathArgv, &resultDString);
1216
    *attributePtrPtr = Tcl_NewStringObj(resultStr, Tcl_DStringLength(&resultDString));
1217
    Tcl_DStringFree(&resultDString);
1218
 
1219
cleanup:
1220
    for (i = 0; i < pathArgc; i++) {
1221
        ckfree(newPathArgv[i]);
1222
    }
1223
    ckfree((char *) newPathArgv);
1224
    return result;
1225
}
1226
 
1227
/*
1228
 *----------------------------------------------------------------------
1229
 *
1230
 * GetWinFileLongName --
1231
 *
1232
 *      Returns a Tcl_Obj containing the short version of the file
1233
 *      name.
1234
 *
1235
 * Results:
1236
 *      Standard Tcl result and a Tcl_Obj in attributePtrPtr. The object
1237
 *      will have ref count 0. If the return value is not TCL_OK,
1238
 *      attributePtrPtr is not touched.
1239
 *
1240
 * Side effects:
1241
 *      A new object is allocated if the file is valid.
1242
 *
1243
 *----------------------------------------------------------------------
1244
 */
1245
 
1246
static int
1247
GetWinFileLongName(
1248
    Tcl_Interp *interp,             /* The interp we are using for errors. */
1249
    int objIndex,                   /* The index of the attribute. */
1250
    char *fileName,                 /* The name of the file. */
1251
    Tcl_Obj **attributePtrPtr)      /* A pointer to return the object with. */
1252
{
1253
    return ConvertFileNameFormat(interp, objIndex, fileName, 1, attributePtrPtr);
1254
}
1255
 
1256
/*
1257
 *----------------------------------------------------------------------
1258
 *
1259
 * GetWinFileShortName --
1260
 *
1261
 *      Returns a Tcl_Obj containing the short version of the file
1262
 *      name.
1263
 *
1264
 * Results:
1265
 *      Standard Tcl result and a Tcl_Obj in attributePtrPtr. The object
1266
 *      will have ref count 0. If the return value is not TCL_OK,
1267
 *      attributePtrPtr is not touched.
1268
 *
1269
 * Side effects:
1270
 *      A new object is allocated if the file is valid.
1271
 *
1272
 *----------------------------------------------------------------------
1273
 */
1274
 
1275
static int
1276
GetWinFileShortName(
1277
    Tcl_Interp *interp,             /* The interp we are using for errors. */
1278
    int objIndex,                   /* The index of the attribute. */
1279
    char *fileName,                 /* The name of the file. */
1280
    Tcl_Obj **attributePtrPtr)      /* A pointer to return the object with. */
1281
{
1282
    return ConvertFileNameFormat(interp, objIndex, fileName, 0, attributePtrPtr);
1283
}
1284
 
1285
/*
1286
 *----------------------------------------------------------------------
1287
 *
1288
 * SetWinFileAttributes --
1289
 *
1290
 *      Set the file attributes to the value given by attributePtr.
1291
 *      This routine sets the -hidden, -readonly, or -system attributes.
1292
 *
1293
 * Results:
1294
 *      Standard TCL error.
1295
 *
1296
 * Side effects:
1297
 *      The file's attribute is set.
1298
 *
1299
 *----------------------------------------------------------------------
1300
 */
1301
 
1302
static int
1303
SetWinFileAttributes(
1304
    Tcl_Interp *interp,             /* The interp we are using for errors. */
1305
    int objIndex,                   /* The index of the attribute. */
1306
    char *fileName,                 /* The name of the file. */
1307
    Tcl_Obj *attributePtr)          /* The new value of the attribute. */
1308
{
1309
    DWORD fileAttributes = GetFileAttributes(fileName);
1310
    int yesNo;
1311
    int result;
1312
 
1313
    if (fileAttributes == 0xFFFFFFFF) {
1314
        AttributesPosixError(interp, objIndex, fileName, 1);
1315
        return TCL_ERROR;
1316
    }
1317
 
1318
    result = Tcl_GetBooleanFromObj(interp, attributePtr, &yesNo);
1319
    if (result != TCL_OK) {
1320
        return result;
1321
    }
1322
 
1323
    if (yesNo) {
1324
        fileAttributes |= (attributeArray[objIndex]);
1325
    } else {
1326
        fileAttributes &= ~(attributeArray[objIndex]);
1327
    }
1328
 
1329
    if (!SetFileAttributes(fileName, fileAttributes)) {
1330
        AttributesPosixError(interp, objIndex, fileName, 1);
1331
        return TCL_ERROR;
1332
    }
1333
    return TCL_OK;
1334
}
1335
 
1336
/*
1337
 *----------------------------------------------------------------------
1338
 *
1339
 * SetWinFileLongName --
1340
 *
1341
 *      The attribute in question is a readonly attribute and cannot
1342
 *      be set.
1343
 *
1344
 * Results:
1345
 *      TCL_ERROR
1346
 *
1347
 * Side effects:
1348
 *      The object result is set to a pertinant error message.
1349
 *
1350
 *----------------------------------------------------------------------
1351
 */
1352
 
1353
static int
1354
CannotSetAttribute(
1355
    Tcl_Interp *interp,             /* The interp we are using for errors. */
1356
    int objIndex,                   /* The index of the attribute. */
1357
    char *fileName,                 /* The name of the file. */
1358
    Tcl_Obj *attributePtr)          /* The new value of the attribute. */
1359
{
1360
    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1361
            "cannot set attribute \"", tclpFileAttrStrings[objIndex],
1362
            "\" for file \"", fileName, "\" : attribute is readonly",
1363
            (char *) NULL);
1364
    return TCL_ERROR;
1365
}
1366
 
1367
 
1368
/*
1369
 *---------------------------------------------------------------------------
1370
 *
1371
 * TclpListVolumes --
1372
 *
1373
 *      Lists the currently mounted volumes
1374
 *
1375
 * Results:
1376
 *      A standard Tcl result.  Will always be TCL_OK, since there is no way
1377
 *      that this command can fail.  Also, the interpreter's result is set to
1378
 *      the list of volumes.
1379
 *
1380
 * Side effects:
1381
 *      None
1382
 *
1383
 *---------------------------------------------------------------------------
1384
 */
1385
 
1386
int
1387
TclpListVolumes(
1388
    Tcl_Interp *interp)    /* Interpreter to which to pass the volume list */
1389
{
1390
    Tcl_Obj *resultPtr, *elemPtr;
1391
    char buf[4];
1392
    int i;
1393
 
1394
    resultPtr = Tcl_GetObjResult(interp);
1395
 
1396
    buf[1] = ':';
1397
    buf[2] = '/';
1398
    buf[3] = '\0';
1399
 
1400
    /*
1401
     * On Win32s:
1402
     * GetLogicalDriveStrings() isn't implemented.
1403
     * GetLogicalDrives() returns incorrect information.
1404
     */
1405
 
1406
    for (i = 0; i < 26; i++) {
1407
        buf[0] = (char) ('a' + i);
1408
        if (GetVolumeInformation(buf, NULL, 0, NULL, NULL, NULL, NULL, 0)
1409
                || (GetLastError() == ERROR_NOT_READY)) {
1410
            elemPtr = Tcl_NewStringObj(buf, -1);
1411
            Tcl_ListObjAppendElement(NULL, resultPtr, elemPtr);
1412
        }
1413
    }
1414
    return TCL_OK;
1415
}

powered by: WebSVN 2.1.0

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