3 IMPORT S
:= SYSTEM
, Kernel
, HostLang
, Files
, Log
, stdlib
:= PosixCstdlib
,
4 unistd
:= PosixCunistd
, dirent
:= PosixCdirent
, fcntl
:= PosixCfcntl
,
5 sysstat
:= PosixCsys_stat
, stdio
:= PosixCstdio
, errno
:= PosixCerrno
,
6 macro
:= PosixCmacro
, libgen
:= PosixClibgen
, time
:= PosixCtime
;
8 (* !!! add buffer cache *)
9 (* !!! HostFiles64 must be base for HostFiles *)
12 closed
= 0; new
= 1; temp
= 2; shared
= 3; exclusive
= 4;
15 FullName
* = Files
.Name
;
16 NativeName
* = ARRAY 1024 OF SHORTCHAR
;
18 Locator
= POINTER TO RECORD (Files
.Locator
)
19 path
-: FullName (* pathname # "" *)
22 Directory
= POINTER TO RECORD (Files
.Directory
) END;
24 File
= POINTER TO RECORD (Files
.File
)
26 len
: INTEGER; (* !!! must be sysstat.off_t *)
32 Reader
= POINTER TO RECORD (Files
.Reader
)
37 Writer
= POINTER TO RECORD (Files
.Writer
)
42 InodeIdentifier
= RECORD (Kernel
.Identifier
)
50 PROCEDURE (VAR id
: InodeIdentifier
) Identified (): BOOLEAN;
54 RETURN (f
.state
# closed
) & (f
.ino
= id
.ino
)
57 PROCEDURE GetFileByInode (ino
: sysstat
.ino_t
): File
;
58 VAR id
: InodeIdentifier
; obj
: ANYPTR
; f
: File
;
61 id
.typ
:= S
.TYP(File
);
63 obj
:= Kernel
.ThisFinObj(id
);
64 IF obj
# NIL THEN f
:= obj(File
)
70 PROCEDURE GetError (OUT res
: INTEGER);
75 | errno
.ENAMETOOLONG
, errno
.ENOTDIR
: res
:= 1 (* invalid name/location *)
76 | errno
.ENOENT
: res
:= 2 (* file/dir not found *)
77 | errno
.EEXIST
: res
:= 3 (* file/dir already exists *)
78 | errno
.EROFS
: res
:= 4 (* write-protection *)
79 | errno
.EIO
: res
:= 5 (* io error *)
80 | errno
.EACCES
, errno
.EPERM
: res
:= 6 (* access denied *)
81 | errno
.ENOMEM
: res
:= 80 (* not enough memory *)
82 | errno
.ENFILE
, errno
.ENOBUFS
, errno
.ENOSPC
: res
:= 81 (* not enough system resources *)
89 PROCEDURE NewLocator
* (IN path
: ARRAY OF CHAR): Locator
;
90 VAR l
: Locator
; ch
: SHORTCHAR
;
93 IF path
= "" THEN l
.path
:= "."
99 PROCEDURE (l
: Locator
) This (IN path
: ARRAY OF CHAR): Locator
;
102 IF path
= "" THEN NEW(loc
); loc^
:= l^
103 ELSIF path
[0] = "/" THEN loc
:= NewLocator(path
)
104 ELSE loc
:= NewLocator(l
.path
+ "/" + path
)
111 PROCEDURE (f
: File
) Length (): INTEGER;
116 PROCEDURE (f
: File
) NewReader (old
: Files
.Reader
): Reader
;
119 ASSERT(f
.state
# closed
, 20);
120 IF (old
# NIL) & (old
.Base() = f
) THEN
122 IF r
.pos
> f
.len
THEN r
.pos
:= 0 END;
124 ELSE NEW(r
); r
.f
:= f
; r
.pos
:= 0
129 PROCEDURE (f
: File
) NewWriter (old
: Files
.Writer
): Writer
;
132 ASSERT(f
.state
# closed
, 20);
133 ASSERT(f
.state
# shared
, 21);
134 IF (old
# NIL) & (old
.Base() = f
) THEN
136 IF w
.pos
> f
.len
THEN w
.pos
:= 0 END
137 ELSE NEW(w
); w
.f
:= f
; w
.pos
:= 0
142 PROCEDURE (f
: File
) Flush
;
145 IF f
.state
= exclusive
THEN
146 res
:= unistd
.fsync(f
.fd
);
151 PROCEDURE IsName (IN name
: Files
.Name
): BOOLEAN;
155 WHILE (name
[i
] # "/") & (name
[i
] # 0X
) DO INC(i
) END;
159 PROCEDURE DirName (VAR path
: ARRAY OF CHAR);
160 VAR i
, j
, k
: INTEGER;
162 IF path
[0] = "/" THEN i
:= 1; j
:= 1; k
:= 1
163 ELSE i
:= 0; j
:= 0; k
:= 0
165 WHILE path
[i
] # 0X
DO
166 IF path
[i
] = "/" THEN
167 k
:= j
; j
:= i
; INC(i
);
168 WHILE (path
[i
] # 0X
) & (path
[i
] = "/") DO INC(i
) END;
169 IF path
[i
] = 0X
THEN j
:= k
END
177 PROCEDURE (f
: File
) Register (name
: Files
.Name
; type
: Files
.Type
; ask
: BOOLEAN; OUT res
: INTEGER);
178 VAR i
, err
: INTEGER; dir
: FullName
; p0
, p1
: NativeName
; s
: sysstat
.struct_stat
; x
: unistd
.int
;
180 ASSERT(f
.state
= new
, 20);
181 ASSERT(name
# "", 21);
182 ASSERT(IsName(name
), 22);
183 HostLang
.StringToHost(f
.pathname
, p0
, HostLang
.pep383
, err
);
187 HostLang
.StringToHost(dir
+ "/" + name
, p1
, HostLang
.pep383
, err
);
189 x
:= stdio
.rename(p0
, p1
);
190 IF x
= 0 THEN res
:= 0 (* no error *)
193 f
.state
:= exclusive
;
196 res
:= 1 (* invalid name (too long?) *)
199 res
:= 1 (* invalid name (too long?) *)
203 PROCEDURE (f
: File
) Close
;
204 VAR res
: unistd
.int
; path
: NativeName
; err
: INTEGER;
206 IF f
.state
# closed
THEN
208 IF f
.state
= new
THEN
209 HostLang
.StringToHost(f
.pathname
, path
, HostLang
.pep383
, err
);
210 ASSERT(err
= 0, 100);
211 res
:= unistd
.unlink(path
);
212 ASSERT(res
= 0, 101);
215 res
:= unistd
.close(f
.fd
);
216 ASSERT(res
= 0, 102);
221 PROCEDURE (f
: File
) Closed (): BOOLEAN;
223 RETURN f
.state
= closed
226 PROCEDURE (f
: File
) Shared (): BOOLEAN;
228 RETURN f
.state
= shared
231 PROCEDURE (f
: File
) FINALIZE
;
238 PROCEDURE (r
: Reader
) Base (): File
;
243 PROCEDURE (r
: Reader
) Pos (): INTEGER;
248 PROCEDURE (r
: Reader
) SetPos (pos
: INTEGER);
250 ASSERT(pos
>= 0, 20);
251 ASSERT(pos
<= r
.f
.len
, 21);
256 PROCEDURE (r
: Reader
) ReadByte (OUT x
: BYTE);
257 VAR res
: unistd
.int
; offset
: unistd
.off_t
;
259 ASSERT(r
.f
.state
# closed
, 20);
260 offset
:= unistd
.lseek(r
.f
.fd
, r
.pos
, unistd
.SEEK_SET
);
261 ASSERT(offset
= r
.pos
, 100);
262 res
:= unistd
.read(r
.f
.fd
, S
.ADR(x
), 1);
263 ASSERT(res
# -1, 101);
264 IF res
= 0 THEN x
:= 0 END;
265 r
.pos
:= r
.pos
+ res
;
269 PROCEDURE (r
: Reader
) ReadBytes (VAR x
: ARRAY OF BYTE; beg
, len
: INTEGER);
270 VAR res
: unistd
.int
; offset
: unistd
.off_t
;
272 ASSERT(beg
>= 0, 20);
274 ASSERT(beg
+ len
<= LEN(x
), 22);
275 ASSERT(r
.f
.state
# closed
, 23);
276 offset
:= unistd
.lseek(r
.f
.fd
, r
.pos
, unistd
.SEEK_SET
);
277 ASSERT(offset
= r
.pos
, 100);
278 res
:= unistd
.read(r
.f
.fd
, S
.ADR(x
[beg
]), len
);
279 ASSERT(res
# -1, 101);
280 r
.pos
:= r
.pos
+ res
;
286 PROCEDURE (w
: Writer
) Base (): File
;
291 PROCEDURE (w
: Writer
) Pos (): INTEGER;
296 PROCEDURE (w
: Writer
) SetPos (pos
: INTEGER);
298 ASSERT(pos
>= 0, 20);
299 ASSERT(pos
<= w
.f
.len
, 21);
303 PROCEDURE (w
: Writer
) WriteByte (x
: BYTE);
304 VAR res
: unistd
.int
; offset
: unistd
.off_t
;
306 ASSERT(w
.f
.state
# closed
, 20);
307 offset
:= unistd
.lseek(w
.f
.fd
, w
.pos
, unistd
.SEEK_SET
);
308 ASSERT(offset
= w
.pos
, 100);
309 res
:= unistd
.write(w
.f
.fd
, S
.ADR(x
), 1);
310 ASSERT(res
# -1, 101);
311 w
.pos
:= w
.pos
+ res
;
312 w
.f
.len
:= MAX(w
.f
.len
, w
.pos
);
316 PROCEDURE (w
: Writer
) WriteBytes (IN x
: ARRAY OF BYTE; beg
, len
: INTEGER);
317 VAR res
: unistd
.int
; offset
: unistd
.off_t
;
319 ASSERT(beg
>= 0, 20);
320 ASSERT(len
>= 0, 21);
321 ASSERT(beg
+ len
<= LEN(x
), 22);
322 ASSERT(w
.f
.state
# closed
, 23);
323 offset
:= unistd
.lseek(w
.f
.fd
, w
.pos
, unistd
.SEEK_SET
);
324 ASSERT(offset
= w
.pos
, 100);
325 res
:= unistd
.write(w
.f
.fd
, S
.ADR(x
[beg
]), len
);
326 ASSERT(res
# -1, 101);
327 w
.pos
:= w
.pos
+ res
;
328 w
.f
.len
:= MAX(w
.f
.len
, w
.pos
);
329 ASSERT(res
= len
, 60)
334 PROCEDURE (d
: Directory
) This (IN path
: ARRAY OF CHAR): Locator
;
336 RETURN root
.This(path
)
339 PROCEDURE MakeDir (path
: ARRAY OF SHORTCHAR
; OUT res
: unistd
.int
);
340 VAR i
: INTEGER; sep
: BOOLEAN; err
: unistd
.int
; s
: sysstat
.struct_stat
; mode
: sysstat
.mode_t
;
343 mode
:= S
.VAL((*!!!*)sysstat
.mode_t
, ORD(BITS(511(*a=rwx*)) - BITS(sysstat
.umask(0))));
344 WHILE (err
= 0) & (path
[i
] # 0X
) DO
345 WHILE (path
[i
] # "/") & (path
[i
] # 0X
) DO INC(i
) END;
346 sep
:= path
[i
] = "/";
347 IF sep
THEN path
[i
] := 0X
END;
348 err
:= sysstat
.mkdir(path
, mode
);
352 (* already exists, continue make dirs *)
356 IF sep
THEN path
[i
] := "/" END;
362 PROCEDURE (d
: Directory
) New (loc
: Files
.Locator
; ask
: BOOLEAN): File
;
363 VAR err
: INTEGER; f
: File
; s
: sysstat
.struct_stat
; fd
, res
: unistd
.int
; pathname
: NativeName
;
365 ASSERT(loc
# NIL, 20);
367 HostLang
.StringToHost(loc
.path
, pathname
, HostLang
.pep383
, err
);
369 MakeDir(pathname
, res
);
371 (* use fcntl.open() with O_TMPFILE for Linux 3.11+? *)
372 pathname
:= pathname
+ "/" + ".newXXXXXX";
373 fd
:= stdlib
.mkstemp(pathname
);
375 NEW(f
); HostLang
.HostToString(pathname
, f
.pathname
, HostLang
.pep383
, err
);
377 (* !!! get valid inode? *)
378 f
.fd
:= fd
; f
.len
:= 0; f
.state
:= new
; f
.ino
:= 0;
379 loc
.res
:= 0 (* no errors *)
382 res
:= unistd
.close(fd
);
383 ASSERT(res
= 0, 100);
384 res
:= unistd
.unlink(pathname
);
385 ASSERT(res
= 0, 101);
386 loc
.res
:= 1 (* invalid name *)
395 loc
.res
:= 1 (* invalid name *)
398 loc
.res
:= 1 (* invalid locator *)
403 PROCEDURE IsRegFile (IN s
: sysstat
.struct_stat
): BOOLEAN;
405 RETURN BITS(s
.st_mode
) * BITS(sysstat
.S_IFMT
) = BITS(sysstat
.S_IFREG
)
408 PROCEDURE (d
: Directory
) Old (loc
: Files
.Locator
; name
: Files
.Name
; isShared
: BOOLEAN): File
;
410 VAR err
: INTEGER; f
, if
: File
; s
: sysstat
.struct_stat
; fd
, flags
, res
: unistd
.int
;
411 pathname
: NativeName
; mode
: sysstat
.mode_t
; lock
: fcntl
.struct_flock
;
416 res
:= unistd
.close(fd
);
421 ASSERT(loc
# NIL, 20);
422 ASSERT(name
# "", 21);
425 HostLang
.StringToHost(loc
.path
+ "/" + name
, pathname
, HostLang
.pep383
, err
);
427 res
:= macro
.stat(pathname
, s
);
430 if
:= GetFileByInode(s
.st_ino
);
431 IF (if
= NIL) OR isShared
& (if
.state
= shared
) THEN
432 mode
:= S
.VAL((*!!!*)sysstat
.mode_t
, ORD(BITS(rwrwrw
) - BITS(sysstat
.umask(0))));
433 IF isShared
THEN flags
:= fcntl
.O_RDONLY
434 ELSE flags
:= fcntl
.O_RDWR
436 fd
:= fcntl
.open(pathname
, flags
, mode
);
438 IF isShared
THEN lock
.l_type
:= fcntl
.F_RDLCK
439 ELSE lock
.l_type
:= fcntl
.F_WRLCK
441 lock
.l_whence
:= unistd
.SEEK_SET
;
445 res
:= fcntl
.fcntl(fd
, fcntl
.F_SETLK
, S
.ADR(lock
));
447 NEW(f
); HostLang
.HostToString(pathname
, f
.pathname
, HostLang
.pep383
, err
);
449 f
.fd
:= fd
; f
.len
:= S
.VAL((*!!!*)INTEGER, s
.st_size
); f
.ino
:= s
.st_ino
;
450 IF isShared
THEN f
.state
:= shared
451 ELSE f
.state
:= exclusive
453 loc
.res
:= 0 (* no errors *)
455 loc
.res
:= 1; (* invalid name *)
459 GetError(loc
.res
); (* already locked *)
463 GetError(loc
.res
) (* failed to open *)
466 loc
.res
:= 6 (* already opened / locked *)
469 loc
.res
:= 6 (* access denied (not a regular file) *)
472 loc
.res
:= 2 (* file not found *)
475 loc
.res
:= 1 (* invalid name *)
478 loc
.res
:= 1 (* invalid name *)
481 loc
.res
:= 1 (* invalid locator *)
486 PROCEDURE (d
: Directory
) Temp (): File
;
487 VAR f
: File
; fd
: unistd
.int
; name
: ARRAY 12 OF SHORTCHAR
;
489 (* use fcntl.open() with O_TMPFILE for Linux 3.11+? *)
490 name
:= ".tmpXXXXXX";
491 fd
:= stdlib
.mkstemp(name
);
492 ASSERT(fd
# -1, 100);
493 (* !!! get pathname and unlink it here *)
494 NEW(f
); f
.fd
:= fd
; f
.pathname
:= ""; f
.len
:= 0; f
.ino
:= 0; f
.state
:= temp
;
498 PROCEDURE (d
: Directory
) Delete (loc
: Files
.Locator
; name
: Files
.Name
);
499 VAR pathname
: NativeName
; err
: INTEGER; res
: unistd
.int
;
501 ASSERT(loc
# NIL, 20);
502 ASSERT(IsName(name
), 21);
505 HostLang
.StringToHost(loc
.path
+ "/" + name
, pathname
, HostLang
.pep383
, err
);
507 res
:= unistd
.unlink(pathname
);
508 IF res
= 0 THEN loc
.res
:= 0 (* no error *)
509 ELSE GetError(loc
.res
)
512 loc
.res
:= 1 (* invalid name *)
515 loc
.res
:= 1 (* invalid name *)
518 loc
.res
:= 1 (* invalid locator *)
522 PROCEDURE (d
: Directory
) Rename (loc
: Files
.Locator
; old
, new
: Files
.Name
; ask
: BOOLEAN);
523 VAR p0
, p1
: NativeName
; res
: stdio
.int
; err
: INTEGER;
525 ASSERT(loc
# NIL, 20);
526 ASSERT(old
# "", 21);
527 ASSERT(new
# "", 22);
529 IF IsName(old
) & IsName(new
) THEN
530 HostLang
.StringToHost(loc
.path
+ "/" + old
, p0
, HostLang
.pep383
, err
);
532 HostLang
.StringToHost(loc
.path
+ "/" + new
, p1
, HostLang
.pep383
, err
);
534 res
:= stdio
.rename(p0
, p1
);
535 IF res
= 0 THEN loc
.res
:= 0 (* no error *)
536 ELSE GetError(loc
.res
)
539 loc
.res
:= 1 (* invalid name *)
542 loc
.res
:= 1 (* invalid name *)
545 loc
.res
:= 1 (* invalid name *)
548 loc
.res
:= 1 (* invalid locator *)
552 PROCEDURE (d
: Directory
) SameFile (loc0
: Files
.Locator
; name0
: Files
.Name
; loc1
: Files
.Locator
; name1
: Files
.Name
): BOOLEAN;
553 VAR ok
: BOOLEAN; a0
, a1
: NativeName
; s0
, s1
: sysstat
.struct_stat
; err
: INTEGER;
555 ASSERT(loc0
# NIL, 20);
556 ASSERT(name0
# "", 21);
557 ASSERT(loc1
# NIL, 22);
558 ASSERT(name1
# "", 23);
560 WITH loc0
: Locator
DO
561 WITH loc1
: Locator
DO
562 IF IsName(name0
) & IsName(name1
) THEN
563 HostLang
.StringToHost(loc0
.path
+ "/" + name0
, a0
, HostLang
.pep383
, err
);
565 err
:= macro
.stat(a0
, s0
);
567 HostLang
.StringToHost(loc1
.path
+ "/" + name1
, a1
, HostLang
.pep383
, err
);
569 err
:= macro
.stat(a1
, s1
);
571 ok
:= s0
.st_ino
= s1
.st_ino
577 ELSE (* don't trap *)
579 ELSE (* don't trap *)
584 PROCEDURE IsDir (IN s
: sysstat
.struct_stat
): BOOLEAN;
586 RETURN BITS(s
.st_mode
) * BITS(sysstat
.S_IFMT
) = BITS(sysstat
.S_IFDIR
)
589 PROCEDURE GetAttr (IN path
: NativeName
; IN name
: FullName
; s
: sysstat
.struct_stat
): SET;
593 IF name
[0] = "." THEN INCL(attr
, Files
.hidden
) END;
594 IF BITS(s
.st_mode
) * BITS(sysstat
.S_IXOTH
) # {} THEN INCL(attr
, 16) END;
595 IF BITS(s
.st_mode
) * BITS(sysstat
.S_IWOTH
) # {} THEN INCL(attr
, 17) END;
596 IF BITS(s
.st_mode
) * BITS(sysstat
.S_IROTH
) # {} THEN INCL(attr
, 18) END;
597 IF BITS(s
.st_mode
) * BITS(sysstat
.S_IXGRP
) # {} THEN INCL(attr
, 19) END;
598 IF BITS(s
.st_mode
) * BITS(sysstat
.S_IWGRP
) # {} THEN INCL(attr
, 20) END;
599 IF BITS(s
.st_mode
) * BITS(sysstat
.S_IRGRP
) # {} THEN INCL(attr
, 21) END;
600 IF BITS(s
.st_mode
) * BITS(sysstat
.S_IXUSR
) # {} THEN INCL(attr
, 22) END;
601 IF BITS(s
.st_mode
) * BITS(sysstat
.S_IWUSR
) # {} THEN INCL(attr
, 23) END;
602 IF BITS(s
.st_mode
) * BITS(sysstat
.S_IRUSR
) # {} THEN INCL(attr
, 24) END;
603 IF BITS(s
.st_mode
) * BITS(sysstat
.S_ISVTX
) # {} THEN INCL(attr
, 25) END;
604 IF BITS(s
.st_mode
) * BITS(sysstat
.S_ISGID
) # {} THEN INCL(attr
, 26) END;
605 IF BITS(s
.st_mode
) * BITS(sysstat
.S_ISUID
) # {} THEN INCL(attr
, 27) END;
606 (* !!! better to check real access? *)
607 IF BITS(s
.st_mode
) * BITS(sysstat
.S_IRUSR
) # {} THEN INCL(attr
, Files
.readOnly
) END;
611 PROCEDURE (d
: Directory
) FileList (loc
: Files
.Locator
): Files
.FileInfo
;
613 pathname
: NativeName
;
617 ent
: dirent
.Pstruct_dirent
;
618 s
: sysstat
.struct_stat
;
621 h
, t
: Files
.FileInfo
;
623 ASSERT(loc
# NIL, 20);
625 HostLang
.StringToHost(loc
.path
, pathname
, HostLang
.pep383
, err
);
627 p
:= dirent
.opendir(pathname
);
629 ent
:= dirent
.readdir(p
);
631 HostLang
.HostToString(ent
.d_name
, name
, HostLang
.pep383
, err
);
633 HostLang
.StringToHost(loc
.path
+ "/" + name
, pathname
, HostLang
.pep383
, err
);
635 res
:= macro
.stat(pathname
, s
);
636 IF (res
= 0) & ~
IsDir(s
) THEN
637 IF h
= NIL THEN NEW(h
); t
:= h
638 ELSE NEW(t
.next
); t
:= t
.next
641 t
.type
:= ""; (* ??? *)
642 t
.length
:= S
.VAL((*!!!*)INTEGER, s
.st_size
);
643 tm
:= time
.localtime(s
.st_mtim
.tv_sec
);
645 t
.modified
.year
:= tm
.tm_year
+ 1900;
646 t
.modified
.month
:= tm
.tm_mon
+ 1;
647 t
.modified
.day
:= tm
.tm_mday
;
648 t
.modified
.hour
:= tm
.tm_hour
;
649 t
.modified
.minute
:= tm
.tm_min
;
650 t
.modified
.second
:= tm
.tm_sec
652 t
.attr
:= GetAttr(pathname
, name
, s
)
656 ent
:= dirent
.readdir(p
)
658 res
:= dirent
.closedir(p
);
659 ASSERT(res
= 0, 100);
660 loc
.res
:= 0 (* no error *)
665 loc
.res
:= 1 (* invalid name *)
668 loc
.res
:= 1 (* invalid locator *)
673 PROCEDURE (d
: Directory
) LocList (loc
: Files
.Locator
): Files
.LocInfo
;
675 pathname
: NativeName
;
679 ent
: dirent
.Pstruct_dirent
;
680 s
: sysstat
.struct_stat
;
685 ASSERT(loc
# NIL, 20);
687 HostLang
.StringToHost(loc
.path
, pathname
, HostLang
.pep383
, err
);
689 p
:= dirent
.opendir(pathname
);
691 ent
:= dirent
.readdir(p
);
693 HostLang
.HostToString(ent
.d_name
, name
, HostLang
.pep383
, err
);
695 HostLang
.StringToHost(loc
.path
+ "/" + name
, pathname
, HostLang
.pep383
, err
);
697 res
:= macro
.stat(pathname
, s
);
698 IF (res
= 0) & IsDir(s
) & (name
# ".") & (name
# "..") THEN
699 IF h
= NIL THEN NEW(h
); t
:= h
700 ELSE NEW(t
.next
); t
:= t
.next
703 t
.attr
:= GetAttr(pathname
, name
, s
)
707 ent
:= dirent
.readdir(p
)
709 res
:= dirent
.closedir(p
);
710 ASSERT(res
= 0, 100);
711 loc
.res
:= 0 (* no error *)
716 loc
.res
:= 1 (* invlid name *)
719 loc
.res
:= 1 (* invalid locator *)
724 PROCEDURE (d
: Directory
) GetFileName (name
: Files
.Name
; type
: Files
.Type
; OUT filename
: Files
.Name
);
726 filename
:= name
+ "." + type
731 (* !!! implement NofFiles *)
732 (* !!! implement GetModDate & GetName *)
734 PROCEDURE SetRootDir
* (x
: ARRAY OF CHAR);
736 root
:= NewLocator(x
)
744 PROCEDURE IgnoreAsk
*;
753 NEW(d
); Files
.SetDir(d
)