5
* This file implements the Windows specific portion of file manipulation
6
* subcommands of the "file" command.
8
* Copyright (c) 1996-1997 Sun Microsystems, Inc.
10
* See the file "license.terms" for information on usage and redistribution
11
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13
* SCCS: @(#) SciWinFCmd.c 1.19 97/08/05 15:23:47
24
* The following constants specify the type of callback when
25
* TraverseWinTree() calls the traverseProc()
28
#define DOTREE_PRED 1 /* pre-order directory */
29
#define DOTREE_POSTD 2 /* post-order directory */
30
#define DOTREE_F 3 /* regular file */
32
extern int SciWinGetPlatformId ();
33
extern void SciWinConvertError (DWORD errCode);
34
extern void sciprint (char *fmt,...);
36
* Prototype for the TraverseWinTree callback function.
39
typedef int (TraversalProc) (char *src, char *dst, DWORD attr, int type,
40
Sci_DString * errorPtr);
43
* Declarations for local procedures defined in this file:
47
static int TraversalDelete (char *src, char *dst, DWORD attr,
48
int type, Sci_DString * errorPtr);
49
static int TraverseWinTree (TraversalProc * traverseProc,
50
Sci_DString * sourcePtr, Sci_DString * destPtr,
51
Sci_DString * errorPtr);
53
static int ScipRemoveDirectory (
54
char *path, /* Pathname of directory to be removed. */
55
int recursive, /* If non-zero, removes directories that
56
* are nonempty. Otherwise, will only remove
57
* empty directories. */
58
Sci_DString * errorPtr); /* If non-NULL, initialized DString for
63
*---------------------------------------------------------------------------
65
* SciCreateDirectory --
67
* Creates the specified directory. All parent directories of the
68
* specified directory must already exist. The directory is
69
* automatically created with permissions so that user can access
70
* the new directory and create new files or subdirectories in it.
73
* If the directory was successfully created, returns SCI_OK.
74
* Otherwise the return value is SCI_ERROR and errno is set to
75
* indicate the error. Some possible values for errno are:
77
* EACCES: a parent directory can't be read and/or written.
78
* EEXIST: path already exists.
79
* ENOENT: a parent directory doesn't exist.
82
* A directory is created.
84
*---------------------------------------------------------------------------
89
char *path) /* Pathname of directory to create */
93
if (CreateDirectory (path, NULL) == 0)
95
error = GetLastError ();
96
if (SciWinGetPlatformId () == VER_PLATFORM_WIN32s)
98
if ((error == ERROR_ACCESS_DENIED)
99
&& (GetFileAttributes (path) != (DWORD) - 1))
101
error = ERROR_FILE_EXISTS;
104
SciWinConvertError (error);
109
sciprint ("Cannot create directory %s\r\n", path);
118
*----------------------------------------------------------------------
120
* SciRemoveDirectory --
122
* Removes directory (and its contents, if the recursive flag is set).
125
* If the directory was successfully removed, returns SCI_OK.
126
* Otherwise the return value is SCI_ERROR, errno is set to indicate
127
* the error, and the pathname of the file that caused the error
128
* is stored in errorPtr. Some possible values for errno are:
130
* EACCES: path directory can't be read and/or written.
131
* EEXIST: path is a non-empty directory.
132
* EINVAL: path is root directory or current directory.
133
* ENOENT: path doesn't exist or is "".
134
* ENOTDIR: path is not a directory.
136
* EACCES: path is a char device (nul:, com1:, etc.) (95)
137
* EINVAL: path is a char device (nul:, com1:, etc.) (NT)
140
* Directory removed. If an error occurs, the error will be returned
141
* immediately, and remaining files will not be deleted.
143
*----------------------------------------------------------------------
148
char *path) /* Pathname of directory to be removed. */
150
Sci_DString errorPtr;
151
Sci_DStringInit (&errorPtr);
152
if (ScipRemoveDirectory (path, 1, &errorPtr) == SCI_ERROR)
154
sciprint ("Cannot remove directory %s\r\n", errorPtr.string);
156
Sci_DStringFree (&errorPtr);
163
*---------------------------------------------------------------------------
167
* Removes a single file (not a directory).
170
* If the file was successfully deleted, returns SCI_OK. Otherwise
171
* the return value is SCI_ERROR and errno is set to indicate the
172
* error. Some possible values for errno are:
174
* EACCES: a parent directory can't be read and/or written.
175
* EISDIR: path is a directory.
176
* ENOENT: path doesn't exist or is "".
178
* EACCES: exists an open file already referring to path.
179
* EACCES: path is a char device (nul:, com1:, etc.)
182
* The file is deleted, even if it is read-only.
184
*---------------------------------------------------------------------------
189
char *path) /* Pathname of file to be removed. */
193
if (DeleteFile (path) != FALSE)
197
SciWinConvertError (GetLastError ());
201
* Win32s thinks that "" is the same as "." and then reports EISDIR
207
else if (errno == EACCES)
209
attr = GetFileAttributes (path);
210
if (attr != (DWORD) - 1)
212
if (attr & FILE_ATTRIBUTE_DIRECTORY)
215
* Windows NT reports removing a directory as EACCES instead
221
else if (attr & FILE_ATTRIBUTE_READONLY)
223
SetFileAttributes (path, attr & ~FILE_ATTRIBUTE_READONLY);
224
if (DeleteFile (path) != FALSE)
228
SciWinConvertError (GetLastError ());
229
SetFileAttributes (path, attr);
233
else if (errno == ENOENT)
235
attr = GetFileAttributes (path);
236
if (attr != (DWORD) - 1)
238
if (attr & FILE_ATTRIBUTE_DIRECTORY)
241
* Windows 95 reports removing a directory as ENOENT instead
249
else if (errno == EINVAL)
252
* Windows NT reports removing a char device as EINVAL instead of
263
*----------------------------------------------------------------------
265
* ScipRemoveDirectory --
267
* Removes directory (and its contents, if the recursive flag is set).
270
* If the directory was successfully removed, returns SCI_OK.
271
* Otherwise the return value is SCI_ERROR, errno is set to indicate
272
* the error, and the pathname of the file that caused the error
273
* is stored in errorPtr. Some possible values for errno are:
275
* EACCES: path directory can't be read and/or written.
276
* EEXIST: path is a non-empty directory.
277
* EINVAL: path is root directory or current directory.
278
* ENOENT: path doesn't exist or is "".
279
* ENOTDIR: path is not a directory.
281
* EACCES: path is a char device (nul:, com1:, etc.) (95)
282
* EINVAL: path is a char device (nul:, com1:, etc.) (NT)
285
* Directory removed. If an error occurs, the error will be returned
286
* immediately, and remaining files will not be deleted.
288
*----------------------------------------------------------------------
292
ScipRemoveDirectory (
293
char *path, /* Pathname of directory to be removed. */
294
int recursive, /* If non-zero, removes directories that
295
* are nonempty. Otherwise, will only remove
296
* empty directories. */
297
Sci_DString * errorPtr) /* If non-NULL, initialized DString for
298
* error reporting. */
304
if (RemoveDirectory (path) != FALSE)
308
SciWinConvertError (GetLastError ());
312
* Win32s thinks that "" is the same as "." and then reports EACCES
320
attr = GetFileAttributes (path);
321
if (attr != (DWORD) - 1)
323
if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0)
326
* Windows 95 reports calling RemoveDirectory on a file as an
327
* EACCES, not an ENOTDIR.
334
if (attr & FILE_ATTRIBUTE_READONLY)
336
attr &= ~FILE_ATTRIBUTE_READONLY;
337
if (SetFileAttributes (path, attr) == FALSE)
341
if (RemoveDirectory (path) != FALSE)
345
SciWinConvertError (GetLastError ());
346
SetFileAttributes (path, attr | FILE_ATTRIBUTE_READONLY);
350
* Windows 95 and Win32s report removing a non-empty directory
351
* as EACCES, not EEXIST. If the directory is not empty,
352
* change errno so caller knows what's going on.
355
if (SciWinGetPlatformId () != VER_PLATFORM_WIN32_NT)
358
WIN32_FIND_DATA data;
363
Sci_DStringInit (&buffer);
364
find = Sci_DStringAppend (&buffer, path, -1);
365
len = Sci_DStringLength (&buffer);
366
if ((len > 0) && (find[len - 1] != '\\'))
368
Sci_DStringAppend (&buffer, "\\", 1);
370
find = Sci_DStringAppend (&buffer, "*.*", 3);
371
handle = FindFirstFile (find, &data);
372
if (handle != INVALID_HANDLE_VALUE)
376
if ((strcmp (data.cFileName, ".") != 0)
377
&& (strcmp (data.cFileName, "..") != 0))
380
* Found something in this directory.
386
if (FindNextFile (handle, &data) == FALSE)
393
Sci_DStringFree (&buffer);
397
if (errno == ENOTEMPTY)
400
* The caller depends on EEXIST to signify that the directory is
401
* not empty, not ENOTEMPTY.
406
if ((recursive != 0) && (errno == EEXIST))
409
* The directory is nonempty, but the recursive flag has been
410
* specified, so we recursively remove all the files in the directory.
413
Sci_DStringInit (&buffer);
414
Sci_DStringAppend (&buffer, path, -1);
415
result = TraverseWinTree (TraversalDelete, &buffer, NULL, errorPtr);
416
Sci_DStringFree (&buffer);
421
if (errorPtr != NULL)
423
Sci_DStringAppend (errorPtr, path, -1);
429
*---------------------------------------------------------------------------
433
* Traverse directory tree specified by sourcePtr, calling the function
434
* traverseProc for each file and directory encountered. If destPtr
435
* is non-null, each of name in the sourcePtr directory is appended to
436
* the directory specified by destPtr and passed as the second argument
437
* to traverseProc() .
440
* Standard Sci result.
443
* None caused by TraverseWinTree, however the user specified
444
* traverseProc() may change state. If an error occurs, the error will
445
* be returned immediately, and remaining files will not be processed.
447
*---------------------------------------------------------------------------
452
TraversalProc * traverseProc, /* Function to call for every file and
453
* directory in source hierarchy. */
454
Sci_DString * sourcePtr, /* Pathname of source directory to be
456
Sci_DString * targetPtr, /* Pathname of directory to traverse in
457
* parallel with source directory. */
458
Sci_DString * errorPtr) /* If non-NULL, an initialized DString for
459
* error reporting. */
462
char *source, *target, *errfile;
463
int result, sourceLen, targetLen, sourceLenOriginal, targetLenOriginal;
465
WIN32_FIND_DATA data;
468
source = Sci_DStringValue (sourcePtr);
469
sourceLenOriginal = Sci_DStringLength (sourcePtr);
470
if (targetPtr != NULL)
472
target = Sci_DStringValue (targetPtr);
473
targetLenOriginal = Sci_DStringLength (targetPtr);
478
targetLenOriginal = 0;
483
sourceAttr = GetFileAttributes (source);
484
if (sourceAttr == (DWORD) - 1)
489
if ((sourceAttr & FILE_ATTRIBUTE_DIRECTORY) == 0)
492
* Process the regular file
495
return (*traverseProc) (source, target, sourceAttr, DOTREE_F, errorPtr);
499
* When given the pathname of the form "c:\" (one that already ends
500
* with a backslash), must make sure not to add another "\" to the end
501
* otherwise it will try to access a network drive.
504
sourceLen = sourceLenOriginal;
505
if ((sourceLen > 0) && (source[sourceLen - 1] != '\\'))
507
Sci_DStringAppend (sourcePtr, "\\", 1);
510
source = Sci_DStringAppend (sourcePtr, "*.*", 3);
511
handle = FindFirstFile (source, &data);
512
Sci_DStringSetLength (sourcePtr, sourceLen);
513
if (handle == INVALID_HANDLE_VALUE)
516
* Can't read directory
519
SciWinConvertError (GetLastError ());
524
result = (*traverseProc) (source, target, sourceAttr, DOTREE_PRED, errorPtr);
525
if (result != SCI_OK)
531
if (targetPtr != NULL)
533
targetLen = targetLenOriginal;
534
if ((targetLen > 0) && (target[targetLen - 1] != '\\'))
536
target = Sci_DStringAppend (targetPtr, "\\", 1);
543
if ((strcmp (data.cFileName, ".") != 0)
544
&& (strcmp (data.cFileName, "..") != 0))
547
* Append name after slash, and recurse on the file.
550
Sci_DStringAppend (sourcePtr, data.cFileName, -1);
551
if (targetPtr != NULL)
553
Sci_DStringAppend (targetPtr, data.cFileName, -1);
555
result = TraverseWinTree (traverseProc, sourcePtr, targetPtr,
557
if (result != SCI_OK)
563
* Remove name after slash.
566
Sci_DStringSetLength (sourcePtr, sourceLen);
567
if (targetPtr != NULL)
569
Sci_DStringSetLength (targetPtr, targetLen);
572
if (FindNextFile (handle, &data) == FALSE)
580
* Strip off the trailing slash we added
583
Sci_DStringSetLength (sourcePtr, sourceLenOriginal);
584
source = Sci_DStringValue (sourcePtr);
585
if (targetPtr != NULL)
587
Sci_DStringSetLength (targetPtr, targetLenOriginal);
588
target = Sci_DStringValue (targetPtr);
591
if (result == SCI_OK)
594
* Call traverseProc() on a directory after visiting all the
595
* files in that directory.
598
result = (*traverseProc) (source, target, sourceAttr,
599
DOTREE_POSTD, errorPtr);
604
SciWinConvertError (GetLastError ());
605
if (errorPtr != NULL)
607
Sci_DStringAppend (errorPtr, errfile, -1);
616
*----------------------------------------------------------------------
620
* Called by procedure TraverseWinTree for every file and
621
* directory that it encounters in a directory hierarchy. This
622
* procedure unlinks files, and removes directories after all the
623
* containing files have been processed.
626
* Standard Sci result.
629
* Files or directory specified by src will be deleted. If an
630
* error occurs, the windows error is converted to a Posix error
631
* and errno is set accordingly.
633
*----------------------------------------------------------------------
638
char *src, /* Source pathname. */
639
char *ignore, /* Destination pathname (not used). */
640
DWORD srcAttr, /* File attributes for src (not used). */
641
int type, /* Reason for call - see TraverseWinTree(). */
642
Sci_DString * errorPtr) /* If non-NULL, initialized DString for
648
if (ScipDeleteFile (src) == SCI_OK)
658
if (ScipRemoveDirectory (src, 0, NULL) == SCI_OK)
666
if (errorPtr != NULL)
668
Sci_DStringAppend (errorPtr, src, -1);