wasCSharpSQLite – Blame information for rev
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * FileCmd.java -- |
||
3 | * |
||
4 | * This file contains the Jacl implementation of the built-in Tcl "file" |
||
5 | * command. |
||
6 | * |
||
7 | * Copyright (c) 1997 Cornell University. |
||
8 | * Copyright (c) 1997 Sun Microsystems, Inc. |
||
9 | * |
||
10 | * See the file "license.terms" for information on usage and |
||
11 | * redistribution of this file, and for a DISCLAIMER OF ALL |
||
12 | * WARRANTIES. |
||
13 | * |
||
14 | * Included in SQLite3 port to C# for use in testharness only; 2008 Noah B Hart |
||
15 | * |
||
16 | * RCS @(#) $Id: FileCmd.java,v 1.9 2003/02/03 01:39:02 mdejong Exp $ |
||
17 | * |
||
18 | */ |
||
19 | using System; |
||
20 | using System.IO; |
||
21 | namespace tcl.lang |
||
22 | { |
||
23 | |||
24 | /* |
||
25 | * This class implements the built-in "file" command in Tcl. |
||
26 | */ |
||
27 | |||
28 | class FileCmd : Command |
||
29 | { |
||
30 | |||
31 | /// <summary> Reference to File.listRoots, null when JDK < 1.2</summary> |
||
32 | private static System.Reflection.MethodInfo listRootsMethod; |
||
33 | |||
34 | internal static Type procClass = null; |
||
35 | |||
36 | private static readonly string[] validCmds = new string[] { "atime", "attributes", "channels", "copy", "delete", "dirname", "executable", "exists", "extension", "isdirectory", "isfile", "join", "link", "lstat", "mtime", "mkdir", "nativename", "normalize", "owned", "pathtype", "readable", "readlink", "rename", "rootname", "separator", "size", "split", "stat", "system", "tail", "type", "volumes", "writable" }; |
||
37 | |||
38 | private const int OPT_ATIME = 0; |
||
39 | private const int OPT_ATTRIBUTES = 1; |
||
40 | private const int OPT_CHANNELS = 2; |
||
41 | private const int OPT_COPY = 3; |
||
42 | private const int OPT_DELETE = 4; |
||
43 | private const int OPT_DIRNAME = 5; |
||
44 | private const int OPT_EXECUTABLE = 6; |
||
45 | private const int OPT_EXISTS = 7; |
||
46 | private const int OPT_EXTENSION = 8; |
||
47 | private const int OPT_ISDIRECTORY = 9; |
||
48 | private const int OPT_ISFILE = 10; |
||
49 | private const int OPT_JOIN = 11; |
||
50 | private const int OPT_LINK = 12; |
||
51 | private const int OPT_LSTAT = 13; |
||
52 | private const int OPT_MTIME = 14; |
||
53 | private const int OPT_MKDIR = 15; |
||
54 | private const int OPT_NATIVENAME = 16; |
||
55 | private const int OPT_NORMALIZE = 17; |
||
56 | private const int OPT_OWNED = 18; |
||
57 | private const int OPT_PATHTYPE = 19; |
||
58 | private const int OPT_READABLE = 20; |
||
59 | private const int OPT_READLINK = 21; |
||
60 | private const int OPT_RENAME = 22; |
||
61 | private const int OPT_ROOTNAME = 23; |
||
62 | private const int OPT_SEPARATOR = 24; |
||
63 | private const int OPT_SIZE = 25; |
||
64 | private const int OPT_SPLIT = 26; |
||
65 | private const int OPT_STAT = 27; |
||
66 | private const int OPT_SYSTEM = 28; |
||
67 | private const int OPT_TAIL = 29; |
||
68 | private const int OPT_TYPE = 30; |
||
69 | private const int OPT_VOLUMES = 31; |
||
70 | private const int OPT_WRITABLE = 32; |
||
71 | |||
72 | private static readonly string[] validOptions = new string[] { "-force", "--" }; |
||
73 | |||
74 | private const int OPT_FORCE = 0; |
||
75 | private const int OPT_LAST = 1; |
||
76 | |||
77 | public TCL.CompletionCode cmdProc( Interp interp, TclObject[] argv ) |
||
78 | { |
||
79 | if ( argv.Length < 2 ) |
||
80 | { |
||
81 | throw new TclNumArgsException( interp, 1, argv, "option ?arg ...?" ); |
||
82 | } |
||
83 | |||
84 | int opt = TclIndex.get( interp, argv[1], validCmds, "option", 0 ); |
||
85 | string path; |
||
86 | FileInfo fileObj = null; |
||
87 | |||
88 | switch ( opt ) |
||
89 | { |
||
90 | |||
91 | case OPT_ATIME: |
||
92 | if ( argv.Length != 3 ) |
||
93 | { |
||
94 | throw new TclNumArgsException( interp, 2, argv, "name" ); |
||
95 | } |
||
96 | |||
97 | // FIXME: Currently returns the same thing as MTIME. |
||
98 | // Java does not support retrieval of access time. |
||
99 | |||
100 | |||
101 | |||
102 | fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); |
||
103 | |||
104 | interp.setResult( getMtime( interp, argv[2].ToString(), fileObj ) ); |
||
105 | return TCL.CompletionCode.RETURN; |
||
106 | |||
107 | |||
108 | case OPT_ATTRIBUTES: |
||
109 | if ( argv[3].ToString() == "-readonly" ) |
||
110 | fileSetReadOnly( interp, argv ); |
||
111 | else |
||
112 | throw new TclException( interp, "sorry, \"file attributes\" is not implemented yet" ); |
||
113 | return TCL.CompletionCode.RETURN; |
||
114 | |||
115 | |||
116 | case OPT_CHANNELS: |
||
117 | |||
118 | throw new TclException( interp, "sorry, \"file channels\" is not implemented yet" ); |
||
119 | |||
120 | |||
121 | case OPT_COPY: |
||
122 | fileCopyRename( interp, argv, true ); |
||
123 | return TCL.CompletionCode.RETURN; |
||
124 | |||
125 | |||
126 | case OPT_DELETE: |
||
127 | fileDelete( interp, argv ); |
||
128 | return TCL.CompletionCode.RETURN; |
||
129 | |||
130 | |||
131 | case OPT_DIRNAME: |
||
132 | if ( argv.Length != 3 ) |
||
133 | { |
||
134 | throw new TclNumArgsException( interp, 2, argv, "name" ); |
||
135 | } |
||
136 | |||
137 | path = argv[2].ToString(); |
||
138 | |||
139 | // Return all but the last component. If there is only one |
||
140 | // component, return it if the path was non-relative, otherwise |
||
141 | // return the current directory. |
||
142 | |||
143 | |||
144 | TclObject[] splitArrayObj = TclList.getElements( interp, FileUtil.splitAndTranslate( interp, path ) ); |
||
145 | |||
146 | if ( splitArrayObj.Length > 1 ) |
||
147 | { |
||
148 | interp.setResult( FileUtil.joinPath( interp, splitArrayObj, 0, splitArrayObj.Length - 1 ) ); |
||
149 | } |
||
150 | else if ( ( splitArrayObj.Length == 0 ) || ( FileUtil.getPathType( path ) == FileUtil.PATH_RELATIVE ) ) |
||
151 | { |
||
152 | if ( JACL.PLATFORM == JACL.PLATFORM_MAC ) |
||
153 | { |
||
154 | interp.setResult( ":" ); |
||
155 | } |
||
156 | else |
||
157 | { |
||
158 | interp.setResult( "." ); |
||
159 | } |
||
160 | } |
||
161 | else |
||
162 | { |
||
163 | |||
164 | interp.setResult( splitArrayObj[0].ToString() ); |
||
165 | } |
||
166 | return TCL.CompletionCode.RETURN; |
||
167 | |||
168 | |||
169 | case OPT_EXECUTABLE: |
||
170 | if ( argv.Length != 3 ) |
||
171 | { |
||
172 | throw new TclNumArgsException( interp, 2, argv, "name" ); |
||
173 | } |
||
174 | bool isExe = false; |
||
175 | |||
176 | fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); |
||
177 | |||
178 | // A file must exist to be executable. Directories are always |
||
179 | // executable. |
||
180 | |||
181 | bool tmpBool; |
||
182 | if ( File.Exists( fileObj.FullName ) ) |
||
183 | tmpBool = true; |
||
184 | else |
||
185 | tmpBool = Directory.Exists( fileObj.FullName ); |
||
186 | if ( tmpBool ) |
||
187 | { |
||
188 | isExe = Directory.Exists( fileObj.FullName ); |
||
189 | if ( isExe ) |
||
190 | { |
||
191 | interp.setResult( isExe ); |
||
192 | return TCL.CompletionCode.RETURN; |
||
193 | } |
||
194 | |||
195 | if ( Util.Windows ) |
||
196 | { |
||
197 | // File that ends with .exe, .com, or .bat is executable. |
||
198 | |||
199 | |||
200 | string fileName = argv[2].ToString(); |
||
201 | isExe = ( fileName.EndsWith( ".exe" ) || fileName.EndsWith( ".com" ) || fileName.EndsWith( ".bat" ) ); |
||
202 | } |
||
203 | else if ( Util.Mac ) |
||
204 | { |
||
205 | // FIXME: Not yet implemented on Mac. For now, return true. |
||
206 | // Java does not support executability checking. |
||
207 | |||
208 | isExe = true; |
||
209 | } |
||
210 | else |
||
211 | { |
||
212 | // FIXME: Not yet implemented on Unix. For now, return true. |
||
213 | // Java does not support executability checking. |
||
214 | |||
215 | isExe = true; |
||
216 | } |
||
217 | } |
||
218 | interp.setResult( isExe ); |
||
219 | return TCL.CompletionCode.RETURN; |
||
220 | |||
221 | |||
222 | case OPT_EXISTS: |
||
223 | if ( argv.Length != 3 ) |
||
224 | { |
||
225 | throw new TclNumArgsException( interp, 2, argv, "name" ); |
||
226 | } |
||
227 | |||
228 | fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); |
||
229 | bool tmpBool2; |
||
230 | if ( File.Exists( fileObj.FullName ) ) |
||
231 | tmpBool2 = true; |
||
232 | else |
||
233 | tmpBool2 = Directory.Exists( fileObj.FullName ); |
||
234 | interp.setResult( tmpBool2 ); |
||
235 | return TCL.CompletionCode.RETURN; |
||
236 | |||
237 | |||
238 | case OPT_EXTENSION: |
||
239 | if ( argv.Length != 3 ) |
||
240 | { |
||
241 | throw new TclNumArgsException( interp, 2, argv, "name" ); |
||
242 | } |
||
243 | |||
244 | interp.setResult( getExtension( argv[2].ToString() ) ); |
||
245 | return TCL.CompletionCode.RETURN; |
||
246 | |||
247 | |||
248 | case OPT_ISDIRECTORY: |
||
249 | if ( argv.Length != 3 ) |
||
250 | { |
||
251 | throw new TclNumArgsException( interp, 2, argv, "name" ); |
||
252 | } |
||
253 | |||
254 | fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); |
||
255 | interp.setResult( Directory.Exists( fileObj.FullName ) ); |
||
256 | return TCL.CompletionCode.RETURN; |
||
257 | |||
258 | |||
259 | case OPT_ISFILE: |
||
260 | if ( argv.Length != 3 ) |
||
261 | { |
||
262 | throw new TclNumArgsException( interp, 2, argv, "name" ); |
||
263 | } |
||
264 | |||
265 | fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); |
||
266 | interp.setResult( File.Exists( fileObj.FullName ) ); |
||
267 | return TCL.CompletionCode.RETURN; |
||
268 | |||
269 | |||
270 | case OPT_JOIN: |
||
271 | if ( argv.Length < 3 ) |
||
272 | { |
||
273 | throw new TclNumArgsException( interp, 2, argv, "name ?name ...?" ); |
||
274 | } |
||
275 | interp.setResult( FileUtil.joinPath( interp, argv, 2, argv.Length ) ); |
||
276 | return TCL.CompletionCode.RETURN; |
||
277 | |||
278 | |||
279 | case OPT_LINK: |
||
280 | |||
281 | throw new TclException( interp, "sorry, \"file link\" is not implemented yet" ); |
||
282 | |||
283 | |||
284 | case OPT_LSTAT: |
||
285 | if ( argv.Length != 4 ) |
||
286 | { |
||
287 | throw new TclNumArgsException( interp, 2, argv, "name varName" ); |
||
288 | } |
||
289 | |||
290 | // FIXME: Not yet implemented. |
||
291 | // Java does not support link access. |
||
292 | |||
293 | |||
294 | throw new TclException( interp, "file command with opt " + argv[1].ToString() + " is not yet implemented" ); |
||
295 | |||
296 | |||
297 | |||
298 | case OPT_MTIME: |
||
299 | if ( argv.Length != 3 ) |
||
300 | { |
||
301 | throw new TclNumArgsException( interp, 2, argv, "name" ); |
||
302 | } |
||
303 | |||
304 | fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); |
||
305 | |||
306 | interp.setResult( getMtime( interp, argv[2].ToString(), fileObj ) ); |
||
307 | return TCL.CompletionCode.RETURN; |
||
308 | |||
309 | |||
310 | case OPT_MKDIR: |
||
311 | fileMakeDirs( interp, argv ); |
||
312 | return TCL.CompletionCode.RETURN; |
||
313 | |||
314 | |||
315 | case OPT_NATIVENAME: |
||
316 | if ( argv.Length != 3 ) |
||
317 | { |
||
318 | throw new TclNumArgsException( interp, 2, argv, "name" ); |
||
319 | } |
||
320 | |||
321 | |||
322 | interp.setResult( FileUtil.translateFileName( interp, argv[2].ToString() ) ); |
||
323 | return TCL.CompletionCode.RETURN; |
||
324 | |||
325 | |||
326 | case OPT_NORMALIZE: |
||
327 | |||
328 | throw new TclException( interp, "sorry, \"file normalize\" is not implemented yet" ); |
||
329 | |||
330 | |||
331 | case OPT_OWNED: |
||
332 | if ( argv.Length != 3 ) |
||
333 | { |
||
334 | throw new TclNumArgsException( interp, 2, argv, "name" ); |
||
335 | } |
||
336 | |||
337 | fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); |
||
338 | interp.setResult( isOwner( interp, fileObj ) ); |
||
339 | return TCL.CompletionCode.RETURN; |
||
340 | |||
341 | |||
342 | case OPT_PATHTYPE: |
||
343 | if ( argv.Length != 3 ) |
||
344 | { |
||
345 | throw new TclNumArgsException( interp, 2, argv, "name" ); |
||
346 | } |
||
347 | |||
348 | switch ( FileUtil.getPathType( argv[2].ToString() ) ) |
||
349 | { |
||
350 | |||
351 | case FileUtil.PATH_RELATIVE: |
||
352 | interp.setResult( "relative" ); |
||
353 | return TCL.CompletionCode.RETURN; |
||
354 | |||
355 | case FileUtil.PATH_VOLUME_RELATIVE: |
||
356 | interp.setResult( "volumerelative" ); |
||
357 | return TCL.CompletionCode.RETURN; |
||
358 | |||
359 | case FileUtil.PATH_ABSOLUTE: |
||
360 | interp.setResult( "absolute" ); |
||
361 | break; |
||
362 | } |
||
363 | return TCL.CompletionCode.RETURN; |
||
364 | |||
365 | |||
366 | case OPT_READABLE: |
||
367 | if ( argv.Length != 3 ) |
||
368 | { |
||
369 | throw new TclNumArgsException( interp, 2, argv, "name" ); |
||
370 | } |
||
371 | |||
372 | fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); |
||
373 | |||
374 | // interp.setResult(fileObj.canRead()); |
||
375 | // HACK |
||
376 | interp.setResult( true ); |
||
377 | return TCL.CompletionCode.RETURN; |
||
378 | |||
379 | |||
380 | case OPT_READLINK: |
||
381 | if ( argv.Length != 3 ) |
||
382 | { |
||
383 | throw new TclNumArgsException( interp, 2, argv, "name" ); |
||
384 | } |
||
385 | |||
386 | // FIXME: Not yet implemented. |
||
387 | // Java does not support link access. |
||
388 | |||
389 | |||
390 | throw new TclException( interp, "file command with opt " + argv[1].ToString() + " is not yet implemented" ); |
||
391 | |||
392 | |||
393 | case OPT_RENAME: |
||
394 | fileCopyRename( interp, argv, false ); |
||
395 | return TCL.CompletionCode.RETURN; |
||
396 | |||
397 | |||
398 | case OPT_ROOTNAME: |
||
399 | if ( argv.Length != 3 ) |
||
400 | { |
||
401 | throw new TclNumArgsException( interp, 2, argv, "name" ); |
||
402 | } |
||
403 | |||
404 | string fileName2 = argv[2].ToString(); |
||
405 | string extension = getExtension( fileName2 ); |
||
406 | int diffLength = fileName2.Length - extension.Length; |
||
407 | interp.setResult( fileName2.Substring( 0, ( diffLength ) - ( 0 ) ) ); |
||
408 | return TCL.CompletionCode.RETURN; |
||
409 | |||
410 | |||
411 | case OPT_SEPARATOR: |
||
412 | |||
413 | throw new TclException( interp, "sorry, \"file separator\" is not implemented yet" ); |
||
414 | |||
415 | |||
416 | case OPT_SIZE: |
||
417 | if ( argv.Length != 3 ) |
||
418 | { |
||
419 | throw new TclNumArgsException( interp, 2, argv, "name" ); |
||
420 | } |
||
421 | |||
422 | fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); |
||
423 | bool tmpBool3; |
||
424 | if ( File.Exists( fileObj.FullName ) ) |
||
425 | tmpBool3 = true; |
||
426 | else |
||
427 | tmpBool3 = Directory.Exists( fileObj.FullName ); |
||
428 | if ( !tmpBool3 ) |
||
429 | { |
||
430 | |||
431 | throw new TclPosixException( interp, TclPosixException.ENOENT, true, "could not read \"" + argv[2].ToString() + "\"" ); |
||
432 | } |
||
433 | interp.setResult( (int)SupportClass.FileLength( fileObj ) ); |
||
434 | return TCL.CompletionCode.RETURN; |
||
435 | |||
436 | |||
437 | case OPT_SPLIT: |
||
438 | if ( argv.Length != 3 ) |
||
439 | { |
||
440 | throw new TclNumArgsException( interp, 2, argv, "name" ); |
||
441 | } |
||
442 | |||
443 | interp.setResult( FileUtil.splitPath( interp, argv[2].ToString() ) ); |
||
444 | return TCL.CompletionCode.RETURN; |
||
445 | |||
446 | |||
447 | case OPT_STAT: |
||
448 | if ( argv.Length != 4 ) |
||
449 | { |
||
450 | throw new TclNumArgsException( interp, 2, argv, "name varName" ); |
||
451 | } |
||
452 | |||
453 | getAndStoreStatData( interp, argv[2].ToString(), argv[3].ToString() ); |
||
454 | return TCL.CompletionCode.RETURN; |
||
455 | |||
456 | |||
457 | case OPT_SYSTEM: |
||
458 | |||
459 | throw new TclException( interp, "sorry, \"file system\" is not implemented yet" ); |
||
460 | |||
461 | |||
462 | case OPT_TAIL: |
||
463 | if ( argv.Length != 3 ) |
||
464 | { |
||
465 | throw new TclNumArgsException( interp, 2, argv, "name" ); |
||
466 | } |
||
467 | |||
468 | interp.setResult( getTail( interp, argv[2].ToString() ) ); |
||
469 | return TCL.CompletionCode.RETURN; |
||
470 | |||
471 | |||
472 | case OPT_TYPE: |
||
473 | if ( argv.Length != 3 ) |
||
474 | { |
||
475 | throw new TclNumArgsException( interp, 2, argv, "name" ); |
||
476 | } |
||
477 | |||
478 | fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); |
||
479 | |||
480 | interp.setResult( getType( interp, argv[2].ToString(), fileObj ) ); |
||
481 | return TCL.CompletionCode.RETURN; |
||
482 | |||
483 | |||
484 | case OPT_VOLUMES: |
||
485 | if ( argv.Length != 2 ) |
||
486 | { |
||
487 | throw new TclNumArgsException( interp, 2, argv, null ); |
||
488 | } |
||
489 | |||
490 | // use Java 1.2's File.listRoots() method if available |
||
491 | |||
492 | if ( listRootsMethod == null ) |
||
493 | throw new TclException( interp, "\"file volumes\" is not supported" ); |
||
494 | |||
495 | try |
||
496 | { |
||
497 | FileInfo[] roots = (FileInfo[])listRootsMethod.Invoke( null, (System.Object[])new System.Object[0] ); |
||
498 | if ( roots != null ) |
||
499 | { |
||
500 | TclObject list = TclList.newInstance(); |
||
501 | for ( int i = 0; i < roots.Length; i++ ) |
||
502 | { |
||
503 | string root = roots[i].FullName; |
||
504 | TclList.append( interp, list, TclString.newInstance( root ) ); |
||
505 | } |
||
506 | interp.setResult( list ); |
||
507 | } |
||
508 | } |
||
509 | catch ( System.UnauthorizedAccessException ex ) |
||
510 | { |
||
511 | throw new TclRuntimeError( "IllegalAccessException in volumes cmd" ); |
||
512 | } |
||
513 | catch ( System.ArgumentException ex ) |
||
514 | { |
||
515 | throw new TclRuntimeError( "IllegalArgumentException in volumes cmd" ); |
||
516 | } |
||
517 | catch ( System.Reflection.TargetInvocationException ex ) |
||
518 | { |
||
519 | System.Exception t = ex.GetBaseException(); |
||
520 | |||
521 | if ( t is System.ApplicationException ) |
||
522 | { |
||
523 | throw (System.ApplicationException)t; |
||
524 | } |
||
525 | else |
||
526 | { |
||
527 | throw new TclRuntimeError( "unexected exception in volumes cmd" ); |
||
528 | } |
||
529 | } |
||
530 | |||
531 | return TCL.CompletionCode.RETURN; |
||
532 | |||
533 | case OPT_WRITABLE: |
||
534 | if ( argv.Length != 3 ) |
||
535 | { |
||
536 | throw new TclNumArgsException( interp, 2, argv, "name" ); |
||
537 | } |
||
538 | |||
539 | fileObj = FileUtil.getNewFileObj( interp, argv[2].ToString() ); |
||
540 | interp.setResult( SupportClass.FileCanWrite( fileObj ) ); |
||
541 | return TCL.CompletionCode.RETURN; |
||
542 | |||
543 | default: |
||
544 | |||
545 | throw new TclRuntimeError( "file command with opt " + argv[1].ToString() + " is not implemented" ); |
||
546 | |||
547 | } |
||
548 | } |
||
549 | private static bool isOwner( Interp interp, FileInfo fileObj ) |
||
550 | { |
||
551 | // If the file doesn't exist, return false; |
||
552 | |||
553 | bool tmpBool; |
||
554 | if ( File.Exists( fileObj.FullName ) ) |
||
555 | tmpBool = true; |
||
556 | else |
||
557 | tmpBool = Directory.Exists( fileObj.FullName ); |
||
558 | if ( !tmpBool ) |
||
559 | { |
||
560 | return false; |
||
561 | } |
||
562 | bool owner = true; |
||
563 | |||
564 | // For Windows and Macintosh, there are no user ids |
||
565 | // associated with a file, so we always return 1. |
||
566 | |||
567 | if ( Util.Unix ) |
||
568 | { |
||
569 | // FIXME: Not yet implemented on Unix. Do no checking, for now. |
||
570 | // Java does not support ownership checking. |
||
571 | } |
||
572 | return owner; |
||
573 | } |
||
574 | private static int getMtime( Interp interp, string fileName, FileInfo fileObj ) |
||
575 | { |
||
576 | bool tmpBool; |
||
577 | if ( File.Exists( fileObj.FullName ) ) |
||
578 | tmpBool = true; |
||
579 | else |
||
580 | tmpBool = Directory.Exists( fileObj.FullName ); |
||
581 | if ( !tmpBool ) |
||
582 | { |
||
583 | throw new TclPosixException( interp, TclPosixException.ENOENT, true, "could not read \"" + fileName + "\"" ); |
||
584 | } |
||
585 | // Divide to convert msecs to seconds |
||
586 | return (int)( fileObj.LastWriteTime.Ticks / 1000 ); |
||
587 | } |
||
588 | private static string getType( Interp interp, string fileName, FileInfo fileObj ) |
||
589 | { |
||
590 | bool tmpBool; |
||
591 | if ( File.Exists( fileObj.FullName ) ) |
||
592 | tmpBool = true; |
||
593 | else |
||
594 | tmpBool = Directory.Exists( fileObj.FullName ); |
||
595 | if ( !tmpBool ) |
||
596 | { |
||
597 | throw new TclPosixException( interp, TclPosixException.ENOENT, true, "could not read \"" + fileName + "\"" ); |
||
598 | } |
||
599 | |||
600 | if ( File.Exists( fileObj.FullName ) ) |
||
601 | { |
||
602 | return "file"; |
||
603 | } |
||
604 | else if ( Directory.Exists( fileObj.FullName ) ) |
||
605 | { |
||
606 | return "directory"; |
||
607 | } |
||
608 | return "link"; |
||
609 | } |
||
610 | private static void getAndStoreStatData( Interp interp, string fileName, string varName ) |
||
611 | { |
||
612 | FileInfo fileObj = FileUtil.getNewFileObj( interp, fileName ); |
||
613 | |||
614 | bool tmpBool; |
||
615 | if ( File.Exists( fileObj.FullName ) ) |
||
616 | tmpBool = true; |
||
617 | else |
||
618 | tmpBool = Directory.Exists( fileObj.FullName ); |
||
619 | if ( !tmpBool ) |
||
620 | { |
||
621 | throw new TclPosixException( interp, TclPosixException.ENOENT, true, "could not read \"" + fileName + "\"" ); |
||
622 | } |
||
623 | |||
624 | try |
||
625 | { |
||
626 | int mtime = getMtime( interp, fileName, fileObj ); |
||
627 | TclObject mtimeObj = TclInteger.newInstance( mtime ); |
||
628 | TclObject atimeObj = TclInteger.newInstance( mtime ); |
||
629 | TclObject ctimeObj = TclInteger.newInstance( mtime ); |
||
630 | interp.setVar( varName, "atime", atimeObj, 0 ); |
||
631 | interp.setVar( varName, "ctime", ctimeObj, 0 ); |
||
632 | interp.setVar( varName, "mtime", mtimeObj, 0 ); |
||
633 | } |
||
634 | catch ( System.Security.SecurityException e ) |
||
635 | { |
||
636 | throw new TclException( interp, e.Message ); |
||
637 | } |
||
638 | catch ( TclException e ) |
||
639 | { |
||
640 | throw new TclException( interp, "can't set \"" + varName + "(dev)\": variable isn't array" ); |
||
641 | } |
||
642 | |||
643 | try |
||
644 | { |
||
645 | TclObject sizeObj = TclInteger.newInstance( (int)SupportClass.FileLength( fileObj ) ); |
||
646 | interp.setVar( varName, "size", sizeObj, 0 ); |
||
647 | } |
||
648 | catch ( System.Exception e ) |
||
649 | { |
||
650 | // Do nothing. |
||
651 | } |
||
652 | |||
653 | try |
||
654 | { |
||
655 | TclObject typeObj = TclString.newInstance( getType( interp, fileName, fileObj ) ); |
||
656 | interp.setVar( varName, "type", typeObj, 0 ); |
||
657 | } |
||
658 | catch ( System.Exception e ) |
||
659 | { |
||
660 | } |
||
661 | |||
662 | try |
||
663 | { |
||
664 | TclObject uidObj = TclBoolean.newInstance( isOwner( interp, fileObj ) ); |
||
665 | interp.setVar( varName, "uid", uidObj, 0 ); |
||
666 | } |
||
667 | catch ( TclException e ) |
||
668 | { |
||
669 | // Do nothing. |
||
670 | } |
||
671 | } |
||
672 | private static string getExtension( string path ) |
||
673 | // Path for which we find extension. |
||
674 | { |
||
675 | if ( path.Length < 1 ) |
||
676 | { |
||
677 | return ""; |
||
678 | } |
||
679 | |||
680 | // Set lastSepIndex to the first index in the last component of the path. |
||
681 | |||
682 | int lastSepIndex = -1; |
||
683 | switch ( JACL.PLATFORM ) |
||
684 | { |
||
685 | |||
686 | case JACL.PLATFORM_WINDOWS: |
||
687 | string tmpPath = path.Replace( '\\', '/' ).Replace( ':', '/' ); |
||
688 | lastSepIndex = tmpPath.LastIndexOf( (System.Char)'/' ); |
||
689 | break; |
||
690 | |||
691 | case JACL.PLATFORM_MAC: |
||
692 | lastSepIndex = path.LastIndexOf( (System.Char)':' ); |
||
693 | if ( lastSepIndex == -1 ) |
||
694 | { |
||
695 | lastSepIndex = path.LastIndexOf( (System.Char)'/' ); |
||
696 | } |
||
697 | break; |
||
698 | |||
699 | default: |
||
700 | lastSepIndex = path.LastIndexOf( (System.Char)'/' ); |
||
701 | break; |
||
702 | |||
703 | } |
||
704 | ++lastSepIndex; |
||
705 | |||
706 | // Return "" if the last character is a separator. |
||
707 | |||
708 | if ( lastSepIndex >= path.Length ) |
||
709 | { |
||
710 | return ( "" ); |
||
711 | } |
||
712 | |||
713 | // Find the last dot in the last component of the path. |
||
714 | |||
715 | string lastSep = path.Substring( lastSepIndex ); |
||
716 | int dotIndex = lastSep.LastIndexOf( (System.Char)'.' ); |
||
717 | |||
718 | // Return "" if no dot was found in the file's name. |
||
719 | |||
720 | if ( dotIndex == -1 ) |
||
721 | { |
||
722 | return ""; |
||
723 | } |
||
724 | |||
725 | // In earlier versions, we used to back up to the first period in a series |
||
726 | // so that "foo..o" would be split into "foo" and "..o". This is a |
||
727 | // confusing and usually incorrect behavior, so now we split at the last |
||
728 | // period in the name. |
||
729 | |||
730 | return ( lastSep.Substring( dotIndex ) ); |
||
731 | } |
||
732 | private static string getTail( Interp interp, string path ) |
||
733 | { |
||
734 | // Split the path and return the string form of the last component, |
||
735 | // unless there is only one component which is the root or an absolute |
||
736 | // path. |
||
737 | |||
738 | TclObject splitResult = FileUtil.splitAndTranslate( interp, path ); |
||
739 | |||
740 | int last = TclList.getLength( interp, splitResult ) - 1; |
||
741 | |||
742 | if ( last >= 0 ) |
||
743 | { |
||
744 | if ( ( last > 0 ) || ( FileUtil.getPathType( path ) == FileUtil.PATH_RELATIVE ) ) |
||
745 | { |
||
746 | TclObject tailObj = TclList.index( interp, splitResult, last ); |
||
747 | |||
748 | return tailObj.ToString(); |
||
749 | } |
||
750 | } |
||
751 | return ""; |
||
752 | } |
||
753 | private static void fileMakeDirs( Interp interp, TclObject[] argv ) |
||
754 | { |
||
755 | bool madeDir = false; |
||
756 | |||
757 | for ( int currentDir = 2; currentDir < argv.Length; currentDir++ ) |
||
758 | { |
||
759 | |||
760 | string dirName = argv[currentDir].ToString(); |
||
761 | if ( dirName.Length == 0 ) |
||
762 | { |
||
763 | throw new TclPosixException( interp, TclPosixException.ENOENT, true, "can't create directory \"\"" ); |
||
764 | } |
||
765 | FileInfo dirObj = FileUtil.getNewFileObj( interp, dirName ); |
||
766 | bool tmpBool; |
||
767 | if ( File.Exists( dirObj.FullName ) ) |
||
768 | tmpBool = true; |
||
769 | else |
||
770 | tmpBool = Directory.Exists( dirObj.FullName ); |
||
771 | if ( tmpBool ) |
||
772 | { |
||
773 | // If the directory already exists, do nothing. |
||
774 | if ( Directory.Exists( dirObj.FullName ) ) |
||
775 | { |
||
776 | continue; |
||
777 | } |
||
778 | throw new TclPosixException( interp, TclPosixException.EEXIST, true, "can't create directory \"" + dirName + "\"" ); |
||
779 | } |
||
780 | try |
||
781 | { |
||
782 | Directory.CreateDirectory( dirObj.FullName ); |
||
783 | madeDir = true; |
||
784 | } |
||
785 | catch ( Exception e ) |
||
786 | { |
||
787 | throw new TclException( interp, e.Message ); |
||
788 | } |
||
789 | if ( !madeDir ) |
||
790 | { |
||
791 | throw new TclPosixException( interp, TclPosixException.EACCES, true, "can't create directory \"" + dirName + "\": best guess at reason" ); |
||
792 | } |
||
793 | } |
||
794 | } |
||
795 | private static void fileDelete( Interp interp, TclObject[] argv ) |
||
796 | { |
||
797 | bool force = false; |
||
798 | int firstSource = 2; |
||
799 | |||
800 | for ( bool last = false; ( firstSource < argv.Length ) && ( !last ); firstSource++ ) |
||
801 | { |
||
802 | |||
803 | |||
804 | if ( !argv[firstSource].ToString().StartsWith( "-" ) ) |
||
805 | { |
||
806 | break; |
||
807 | } |
||
808 | int opt = TclIndex.get( interp, argv[firstSource], validOptions, "option", 1 ); |
||
809 | switch ( opt ) |
||
810 | { |
||
811 | |||
812 | case OPT_FORCE: |
||
813 | force = true; |
||
814 | break; |
||
815 | |||
816 | case OPT_LAST: |
||
817 | last = true; |
||
818 | break; |
||
819 | |||
820 | default: |
||
821 | throw new TclRuntimeError( "FileCmd.cmdProc: bad option " + opt + " index to validOptions" ); |
||
822 | |||
823 | } |
||
824 | } |
||
825 | |||
826 | if ( firstSource >= argv.Length ) |
||
827 | { |
||
828 | throw new TclNumArgsException( interp, 2, argv, "?options? file ?file ...?" ); |
||
829 | } |
||
830 | |||
831 | for ( int i = firstSource; i < argv.Length; i++ ) |
||
832 | { |
||
833 | |||
834 | deleteOneFile( interp, argv[i].ToString(), force ); |
||
835 | } |
||
836 | } |
||
837 | private static void deleteOneFile( Interp interp, string fileName, bool force ) |
||
838 | { |
||
839 | if ( fileName == ":memory:" ) |
||
840 | return; |
||
841 | bool isDeleted = true; |
||
842 | FileInfo fileObj = FileUtil.getNewFileObj( interp, fileName ); |
||
843 | |||
844 | // Trying to delete a file that does not exist is not |
||
845 | // considered an error, just a no-op |
||
846 | |||
847 | bool tmpBool; |
||
848 | if ( File.Exists( fileObj.FullName ) ) |
||
849 | tmpBool = true; |
||
850 | else |
||
851 | tmpBool = Directory.Exists( fileObj.FullName ); |
||
852 | if ( ( !tmpBool ) || ( fileName.Length == 0 ) ) |
||
853 | { |
||
854 | return; |
||
855 | } |
||
856 | |||
857 | // If the file is a non-empty directory, recursively delete its children if |
||
858 | // the -force option was chosen. Otherwise, throw an error. |
||
859 | |||
860 | if ( Directory.Exists( fileObj.FullName ) && ( Directory.GetFileSystemEntries( fileObj.FullName ).Length > 0 ) ) |
||
861 | { |
||
862 | if ( force ) |
||
863 | { |
||
864 | string[] fileList = Directory.GetFileSystemEntries( fileObj.FullName ); |
||
865 | for ( int i = 0; i < fileList.Length; i++ ) |
||
866 | { |
||
867 | |||
868 | TclObject[] joinArrayObj = new TclObject[2]; |
||
869 | joinArrayObj[0] = TclString.newInstance( fileName ); |
||
870 | joinArrayObj[1] = TclString.newInstance( fileList[i] ); |
||
871 | |||
872 | string child = FileUtil.joinPath( interp, joinArrayObj, 0, 2 ); |
||
873 | deleteOneFile( interp, child, force ); |
||
874 | } |
||
875 | } |
||
876 | else |
||
877 | { |
||
878 | throw new TclPosixException( interp, TclPosixException.ENOTEMPTY, "error deleting \"" + fileName + "\": directory not empty" ); |
||
879 | } |
||
880 | } |
||
881 | try |
||
882 | { |
||
883 | bool tmpBool2; |
||
884 | if ( File.Exists( fileObj.FullName ) ) |
||
885 | { |
||
886 | fileObj.Attributes = FileAttributes.Normal; |
||
887 | File.Delete( fileObj.FullName ); |
||
888 | tmpBool2 = true; |
||
889 | } |
||
890 | else if ( Directory.Exists( fileObj.FullName ) ) |
||
891 | { |
||
892 | Directory.Delete( fileObj.FullName ); |
||
893 | tmpBool2 = true; |
||
894 | } |
||
895 | else |
||
896 | tmpBool2 = false; |
||
897 | isDeleted = tmpBool2; |
||
898 | } |
||
899 | catch ( IOException e ) |
||
900 | { |
||
901 | throw new TclException( interp, e.Message ); |
||
902 | } |
||
903 | catch ( System.Security.SecurityException e ) |
||
904 | { |
||
905 | throw new TclException( interp, e.Message ); |
||
906 | } |
||
907 | if ( !isDeleted ) |
||
908 | { |
||
909 | throw new TclPosixException( interp, TclPosixException.EACCES, true, "error deleting \"" + fileName + "\": best guess at reason" ); |
||
910 | } |
||
911 | } |
||
912 | private static void fileCopyRename( Interp interp, TclObject[] argv, bool copyFlag ) |
||
913 | { |
||
914 | int firstSource = 2; |
||
915 | bool force = false; |
||
916 | |||
917 | for ( bool last = false; ( firstSource < argv.Length ) && ( !last ); firstSource++ ) |
||
918 | { |
||
919 | |||
920 | |||
921 | if ( !argv[firstSource].ToString().StartsWith( "-" ) ) |
||
922 | { |
||
923 | break; |
||
924 | } |
||
925 | int opt = TclIndex.get( interp, argv[firstSource], validOptions, "option", 1 ); |
||
926 | switch ( opt ) |
||
927 | { |
||
928 | |||
929 | case OPT_FORCE: |
||
930 | force = true; |
||
931 | break; |
||
932 | |||
933 | case OPT_LAST: |
||
934 | last = true; |
||
935 | break; |
||
936 | |||
937 | default: |
||
938 | throw new TclRuntimeError( "FileCmd.cmdProc: bad option " + opt + " index to validOptions" ); |
||
939 | |||
940 | } |
||
941 | } |
||
942 | |||
943 | if ( firstSource >= ( argv.Length - 1 ) ) |
||
944 | { |
||
945 | throw new TclNumArgsException( interp, firstSource, argv, "?options? source ?source ...? target" ); |
||
946 | } |
||
947 | |||
948 | // WARNING: ignoring links because Java does not support them. |
||
949 | |||
950 | int target = argv.Length - 1; |
||
951 | |||
952 | string targetName = argv[target].ToString(); |
||
953 | |||
954 | FileInfo targetObj = FileUtil.getNewFileObj( interp, targetName ); |
||
955 | if ( Directory.Exists( targetObj.FullName ) ) |
||
956 | { |
||
957 | // If the target is a directory, move each source file into target |
||
958 | // directory. Extract the tailname from each source, and append it to |
||
959 | // the end of the target path. |
||
960 | |||
961 | for ( int source = firstSource; source < target; source++ ) |
||
962 | { |
||
963 | |||
964 | |||
965 | string sourceName = argv[source].ToString(); |
||
966 | |||
967 | if ( targetName.Length == 0 ) |
||
968 | { |
||
969 | copyRenameOneFile( interp, sourceName, targetName, copyFlag, force ); |
||
970 | } |
||
971 | else |
||
972 | { |
||
973 | string tailName = getTail( interp, sourceName ); |
||
974 | |||
975 | TclObject[] joinArrayObj = new TclObject[2]; |
||
976 | joinArrayObj[0] = TclString.newInstance( targetName ); |
||
977 | joinArrayObj[1] = TclString.newInstance( tailName ); |
||
978 | |||
979 | string fullTargetName = FileUtil.joinPath( interp, joinArrayObj, 0, 2 ); |
||
980 | |||
981 | copyRenameOneFile( interp, sourceName, fullTargetName, copyFlag, force ); |
||
982 | } |
||
983 | } |
||
984 | } |
||
985 | else |
||
986 | { |
||
987 | // If there is more than 1 source file and the target is not a |
||
988 | // directory, then throw an exception. |
||
989 | |||
990 | if ( firstSource + 1 != target ) |
||
991 | { |
||
992 | string action; |
||
993 | if ( copyFlag ) |
||
994 | { |
||
995 | action = "copying"; |
||
996 | } |
||
997 | else |
||
998 | { |
||
999 | action = "renaming"; |
||
1000 | } |
||
1001 | |||
1002 | throw new TclPosixException( interp, TclPosixException.ENOTDIR, "error " + action + ": target \"" + argv[target].ToString() + "\" is not a directory" ); |
||
1003 | } |
||
1004 | |||
1005 | string sourceName = argv[firstSource].ToString(); |
||
1006 | copyRenameOneFile( interp, sourceName, targetName, copyFlag, force ); |
||
1007 | } |
||
1008 | } |
||
1009 | private static void copyRenameOneFile( Interp interp, string sourceName, string targetName, bool copyFlag, bool force ) |
||
1010 | { |
||
1011 | // Copying or renaming a file onto itself is a no-op if force is chosen, |
||
1012 | // otherwise, it will be caught later as an EEXISTS error. |
||
1013 | |||
1014 | if ( force && sourceName.Equals( targetName ) ) |
||
1015 | { |
||
1016 | return; |
||
1017 | } |
||
1018 | |||
1019 | // Check that the source exists and that if -force was not specified, the |
||
1020 | // target doesn't exist. |
||
1021 | // |
||
1022 | // Prevent copying/renaming a file onto a directory and |
||
1023 | // vice-versa. This is a policy decision based on the fact that |
||
1024 | // existing implementations of copy and rename on all platforms |
||
1025 | // also prevent this. |
||
1026 | |||
1027 | string action; |
||
1028 | if ( copyFlag ) |
||
1029 | { |
||
1030 | action = "copying"; |
||
1031 | } |
||
1032 | else |
||
1033 | { |
||
1034 | action = "renaming"; |
||
1035 | } |
||
1036 | |||
1037 | FileInfo sourceFileObj = FileUtil.getNewFileObj( interp, sourceName ); |
||
1038 | bool tmpBool; |
||
1039 | if ( File.Exists( sourceFileObj.FullName ) ) |
||
1040 | tmpBool = true; |
||
1041 | else |
||
1042 | tmpBool = Directory.Exists( sourceFileObj.FullName ); |
||
1043 | if ( ( !tmpBool ) || ( sourceName.Length == 0 ) ) |
||
1044 | { |
||
1045 | throw new TclPosixException( interp, TclPosixException.ENOENT, true, "error " + action + " \"" + sourceName + "\"" ); |
||
1046 | } |
||
1047 | |||
1048 | if ( targetName.Length == 0 ) |
||
1049 | { |
||
1050 | throw new TclPosixException( interp, TclPosixException.ENOENT, true, "error " + action + " \"" + sourceName + "\" to \"" + targetName + "\"" ); |
||
1051 | } |
||
1052 | FileInfo targetFileObj = FileUtil.getNewFileObj( interp, targetName ); |
||
1053 | bool tmpBool2; |
||
1054 | if ( File.Exists( targetFileObj.FullName ) ) |
||
1055 | tmpBool2 = true; |
||
1056 | else |
||
1057 | tmpBool2 = Directory.Exists( targetFileObj.FullName ); |
||
1058 | if ( tmpBool2 && !force ) |
||
1059 | { |
||
1060 | throw new TclPosixException( interp, TclPosixException.EEXIST, true, "error " + action + " \"" + sourceName + "\" to \"" + targetName + "\"" ); |
||
1061 | } |
||
1062 | |||
1063 | if ( Directory.Exists( sourceFileObj.FullName ) && !Directory.Exists( targetFileObj.FullName ) ) |
||
1064 | { |
||
1065 | throw new TclPosixException( interp, TclPosixException.EISDIR, "can't overwrite file \"" + targetName + "\" with directory \"" + sourceName + "\"" ); |
||
1066 | } |
||
1067 | if ( Directory.Exists( targetFileObj.FullName ) && !Directory.Exists( sourceFileObj.FullName ) ) |
||
1068 | { |
||
1069 | throw new TclPosixException( interp, TclPosixException.EISDIR, "can't overwrite directory \"" + targetName + "\" with file \"" + sourceName + "\"" ); |
||
1070 | } |
||
1071 | |||
1072 | if ( !copyFlag ) |
||
1073 | { |
||
1074 | // Perform the rename procedure. |
||
1075 | |||
1076 | try |
||
1077 | { |
||
1078 | sourceFileObj.MoveTo( targetFileObj.FullName ); |
||
1079 | } |
||
1080 | catch ( Exception e ) |
||
1081 | { |
||
1082 | throw new TclPosixException( interp, TclPosixException.EACCES, true, "error renaming \"" + sourceName + "\" to \"" + targetName + "\"" ); |
||
1083 | } |
||
1084 | // { |
||
1085 | // |
||
1086 | // if (Directory.Exists(targetFileObj.FullName)) |
||
1087 | // { |
||
1088 | // throw new TclPosixException(interp, TclPosixException.EEXIST, true, "error renaming \"" + sourceName + "\" to \"" + targetName + "\""); |
||
1089 | // } |
||
1090 | // |
||
1091 | // throw new TclPosixException(interp, TclPosixException.EACCES, true, "error renaming \"" + sourceName + "\" to \"" + targetName + "\": best guess at reason"); |
||
1092 | // } |
||
1093 | } |
||
1094 | else |
||
1095 | { |
||
1096 | // Perform the copy procedure. |
||
1097 | |||
1098 | try |
||
1099 | { |
||
1100 | sourceFileObj.CopyTo( targetFileObj.FullName, true ); |
||
1101 | } |
||
1102 | catch ( IOException e ) |
||
1103 | { |
||
1104 | throw new TclException( interp, "error copying: " + e.Message ); |
||
1105 | } |
||
1106 | } |
||
1107 | } |
||
1108 | private static void fileSetReadOnly( Interp interp, TclObject[] argv ) |
||
1109 | { |
||
1110 | int firstSource = 2; |
||
1111 | |||
1112 | for ( bool last = false; ( firstSource < argv.Length ) && ( !last ); firstSource++ ) |
||
1113 | { |
||
1114 | if ( !argv[firstSource].ToString().StartsWith( "-" ) ) |
||
1115 | { |
||
1116 | break; |
||
1117 | } |
||
1118 | } |
||
1119 | |||
1120 | if ( firstSource >= argv.Length ) |
||
1121 | { |
||
1122 | throw new TclNumArgsException( interp, 2, argv, "?options? file ?file ...?" ); |
||
1123 | } |
||
1124 | |||
1125 | for ( int i = firstSource; i < argv.Length; i++ ) |
||
1126 | { |
||
1127 | |||
1128 | setReadOnlyOneFile( interp, argv[i].ToString() ); |
||
1129 | } |
||
1130 | } |
||
1131 | private static void setReadOnlyOneFile( Interp interp, string fileName ) |
||
1132 | { |
||
1133 | FileInfo fileObj = FileUtil.getNewFileObj( interp, fileName ); |
||
1134 | try |
||
1135 | { |
||
1136 | fileObj.Attributes = FileAttributes.ReadOnly; |
||
1137 | } |
||
1138 | catch ( IOException e ) |
||
1139 | { |
||
1140 | throw new TclException( interp, e.Message ); |
||
1141 | } |
||
1142 | catch ( System.Security.SecurityException e ) |
||
1143 | { |
||
1144 | throw new TclException( interp, e.Message ); |
||
1145 | } |
||
1146 | } |
||
1147 | static FileCmd() |
||
1148 | { |
||
1149 | { |
||
1150 | // File.listRoots() |
||
1151 | Type[] parameterTypes = new Type[0]; |
||
1152 | try |
||
1153 | { |
||
1154 | listRootsMethod = typeof( FileInfo ).GetMethod( "listRoots", (System.Type[])parameterTypes ); |
||
1155 | } |
||
1156 | catch ( System.MethodAccessException e ) |
||
1157 | { |
||
1158 | listRootsMethod = null; |
||
1159 | } |
||
1160 | } |
||
1161 | } |
||
1162 | } // end FileCmd class |
||
1163 | } |